xref: /haiku/src/servers/debug/DebugServer.cpp (revision a629567a9001547736cfe892cdf992be16868fed)
1 /*
2  * Copyright 2011-2013, 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  */
6 
7 
8 #include <map>
9 
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 
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>
27 
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>
35 
36 #include <util/DoublyLinkedList.h>
37 
38 
39 enum {
40 	kActionKillTeam,
41 	kActionDebugTeam,
42 	kActionSaveReportTeam,
43 	kActionPromptUser
44 };
45 
46 
47 //#define HANDOVER_USE_GDB 1
48 #define HANDOVER_USE_DEBUGGER 1
49 
50 #undef B_TRANSLATION_CONTEXT
51 #define B_TRANSLATION_CONTEXT "DebugServer"
52 
53 #define USE_GUI true
54 	// define to false if the debug server shouldn't use GUI (i.e. an alert)
55 
56 //#define TRACE_DEBUG_SERVER
57 #ifdef TRACE_DEBUG_SERVER
58 #	define TRACE(x) debug_printf x
59 #else
60 #	define TRACE(x) ;
61 #endif
62 
63 
64 using std::map;
65 using std::nothrow;
66 
67 
68 static const char *kSignature = "application/x-vnd.Haiku-debug_server";
69 
70 
71 static status_t
72 action_for_string(const char* action, int32& _action)
73 {
74 	if (strcmp(action, "kill") == 0)
75 		_action = kActionKillTeam;
76 	else if (strcmp(action, "debug") == 0)
77 		_action = kActionDebugTeam;
78 	else if (strcmp(action, "log") == 0
79 		|| strcmp(action, "report") == 0) {
80 		_action = kActionSaveReportTeam;
81 	}
82 	else if (strcasecmp(action, "user") == 0)
83 		_action = kActionPromptUser;
84 	else
85 		return B_BAD_VALUE;
86 
87 	return B_OK;
88 }
89 
90 
91 static bool
92 match_team_name(const char* teamName, const char* parameterName)
93 {
94 	RegExp expressionMatcher;
95 	if (expressionMatcher.SetPattern(parameterName,
96 		RegExp::PATTERN_TYPE_WILDCARD)) {
97 		BString value = teamName;
98 		if (parameterName[0] != '/') {
99 			// the expression in question is a team name match only,
100 			// so we need to extract that.
101 			BPath path(teamName);
102 			if (path.InitCheck() == B_OK)
103 				value = path.Leaf();
104 		}
105 
106 		RegExp::MatchResult match = expressionMatcher.Match(value);
107 		if (match.HasMatched())
108 			return true;
109 	}
110 
111 	return false;
112 }
113 
114 
115 static
116 status_t action_for_team(const char* teamName, int32& _action,
117 	bool& _explicitActionFound)
118 {
119 	status_t error = B_OK;
120 	BPath path;
121 	error = find_directory(B_USER_SETTINGS_DIRECTORY, &path);
122 	if (error != B_OK)
123 		return error;
124 
125 	path.Append("system/debug_server/settings");
126 	BDriverSettings settings;
127 	error = settings.Load(path.Path());
128 	if (error != B_OK)
129 		return error;
130 
131 	int32 tempAction;
132 	if (action_for_string(settings.GetParameterValue("default_action",
133 		"user", "user"), tempAction) == B_OK) {
134 		_action = tempAction;
135 	} else
136 		_action = kActionPromptUser;
137 	_explicitActionFound = false;
138 
139 	BDriverParameter parameter = settings.GetParameter("executable_actions");
140 	for (BDriverParameterIterator iterator = parameter.ParameterIterator();
141 		iterator.HasNext();) {
142 		BDriverParameter child = iterator.Next();
143 		if (!match_team_name(teamName, child.Name()))
144 			continue;
145 
146 		if (child.CountValues() > 0) {
147 			if (action_for_string(child.ValueAt(0), tempAction) == B_OK) {
148 				_action = tempAction;
149 				_explicitActionFound = true;
150 			}
151 		}
152 
153 		break;
154 	}
155 
156 	return B_OK;
157 }
158 
159 
160 static void
161 KillTeam(team_id team, const char *appName = NULL)
162 {
163 	// get a team info to verify the team still lives
164 	team_info info;
165 	if (!appName) {
166 		status_t error = get_team_info(team, &info);
167 		if (error != B_OK) {
168 			debug_printf("debug_server: KillTeam(): Error getting info for "
169 				"team %" B_PRId32 ": %s\n", team, strerror(error));
170 			info.args[0] = '\0';
171 		}
172 
173 		appName = info.args;
174 	}
175 
176 	debug_printf("debug_server: Killing team %" B_PRId32 " (%s)\n", team,
177 		appName);
178 
179 	kill_team(team);
180 }
181 
182 
183 // #pragma mark -
184 
185 
186 class DebugMessage : public DoublyLinkedListLinkImpl<DebugMessage> {
187 public:
188 	DebugMessage()
189 	{
190 	}
191 
192 	void SetCode(debug_debugger_message code)		{ fCode = code; }
193 	debug_debugger_message Code() const				{ return fCode; }
194 
195 	debug_debugger_message_data &Data()				{ return fData; }
196 	const debug_debugger_message_data &Data() const	{ return fData; }
197 
198 private:
199 	debug_debugger_message		fCode;
200 	debug_debugger_message_data	fData;
201 };
202 
203 typedef DoublyLinkedList<DebugMessage>	DebugMessageList;
204 
205 
206 class TeamDebugHandler : public BLocker {
207 public:
208 	TeamDebugHandler(team_id team);
209 	~TeamDebugHandler();
210 
211 	status_t Init(port_id nubPort);
212 
213 	team_id Team() const;
214 
215 	status_t PushMessage(DebugMessage *message);
216 
217 private:
218 	status_t _PopMessage(DebugMessage *&message);
219 
220 	thread_id _EnterDebugger(bool saveReport);
221 	status_t _SetupGDBArguments(BStringList &arguments, bool usingConsoled);
222 	void _KillTeam();
223 
224 	int32 _HandleMessage(DebugMessage *message);
225 
226 	void _LookupSymbolAddress(debug_symbol_lookup_context *lookupContext,
227 		const void *address, char *buffer, int32 bufferSize);
228 	void _PrintStackTrace(thread_id thread);
229 	void _NotifyAppServer(team_id team);
230 	void _NotifyRegistrar(team_id team, bool openAlert, bool stopShutdown);
231 
232 	status_t _InitGUI();
233 
234 	static status_t _HandlerThreadEntry(void *data);
235 	status_t _HandlerThread();
236 
237 	bool _ExecutableNameEquals(const char *name) const;
238 	bool _IsAppServer() const;
239 	bool _IsInputServer() const;
240 	bool _IsRegistrar() const;
241 	bool _IsGUIServer() const;
242 
243 	static const char *_LastPathComponent(const char *path);
244 	static team_id _FindTeam(const char *name);
245 	static bool _AreGUIServersAlive();
246 
247 private:
248 	DebugMessageList		fMessages;
249 	sem_id					fMessageCountSem;
250 	team_id					fTeam;
251 	team_info				fTeamInfo;
252 	char					fExecutablePath[B_PATH_NAME_LENGTH];
253 	thread_id				fHandlerThread;
254 	debug_context			fDebugContext;
255 };
256 
257 
258 class TeamDebugHandlerRoster : public BLocker {
259 private:
260 	TeamDebugHandlerRoster()
261 		:
262 		BLocker("team debug handler roster")
263 	{
264 	}
265 
266 public:
267 	static TeamDebugHandlerRoster *CreateDefault()
268 	{
269 		if (!sRoster)
270 			sRoster = new(nothrow) TeamDebugHandlerRoster;
271 
272 		return sRoster;
273 	}
274 
275 	static TeamDebugHandlerRoster *Default()
276 	{
277 		return sRoster;
278 	}
279 
280 	bool AddHandler(TeamDebugHandler *handler)
281 	{
282 		if (!handler)
283 			return false;
284 
285 		BAutolock _(this);
286 
287 		fHandlers[handler->Team()] = handler;
288 
289 		return true;
290 	}
291 
292 	TeamDebugHandler *RemoveHandler(team_id team)
293 	{
294 		BAutolock _(this);
295 
296 		TeamDebugHandler *handler = NULL;
297 
298 		TeamDebugHandlerMap::iterator it = fHandlers.find(team);
299 		if (it != fHandlers.end()) {
300 			handler = it->second;
301 			fHandlers.erase(it);
302 		}
303 
304 		return handler;
305 	}
306 
307 	TeamDebugHandler *HandlerFor(team_id team)
308 	{
309 		BAutolock _(this);
310 
311 		TeamDebugHandler *handler = NULL;
312 
313 		TeamDebugHandlerMap::iterator it = fHandlers.find(team);
314 		if (it != fHandlers.end())
315 			handler = it->second;
316 
317 		return handler;
318 	}
319 
320 	status_t DispatchMessage(DebugMessage *message)
321 	{
322 		if (!message)
323 			return B_BAD_VALUE;
324 
325 		ObjectDeleter<DebugMessage> messageDeleter(message);
326 
327 		team_id team = message->Data().origin.team;
328 
329 		// get the responsible team debug handler
330 		BAutolock _(this);
331 
332 		TeamDebugHandler *handler = HandlerFor(team);
333 		if (!handler) {
334 			// no handler yet, we need to create one
335 			handler = new(nothrow) TeamDebugHandler(team);
336 			if (!handler) {
337 				KillTeam(team);
338 				return B_NO_MEMORY;
339 			}
340 
341 			status_t error = handler->Init(message->Data().origin.nub_port);
342 			if (error != B_OK) {
343 				delete handler;
344 				KillTeam(team);
345 				return error;
346 			}
347 
348 			if (!AddHandler(handler)) {
349 				delete handler;
350 				KillTeam(team);
351 				return B_NO_MEMORY;
352 			}
353 		}
354 
355 		// hand over the message to it
356 		handler->PushMessage(message);
357 		messageDeleter.Detach();
358 
359 		return B_OK;
360 	}
361 
362 private:
363 	typedef map<team_id, TeamDebugHandler*>	TeamDebugHandlerMap;
364 
365 	static TeamDebugHandlerRoster	*sRoster;
366 
367 	TeamDebugHandlerMap				fHandlers;
368 };
369 
370 
371 TeamDebugHandlerRoster *TeamDebugHandlerRoster::sRoster = NULL;
372 
373 
374 class DebugServer : public BServer {
375 public:
376 	DebugServer(status_t &error);
377 
378 	status_t Init();
379 
380 	virtual bool QuitRequested();
381 
382 private:
383 	static status_t _ListenerEntry(void *data);
384 	status_t _Listener();
385 
386 	void _DeleteTeamDebugHandler(TeamDebugHandler *handler);
387 
388 private:
389 	typedef map<team_id, TeamDebugHandler*>	TeamDebugHandlerMap;
390 
391 	port_id				fListenerPort;
392 	thread_id			fListener;
393 	bool				fTerminating;
394 };
395 
396 
397 // #pragma mark -
398 
399 
400 TeamDebugHandler::TeamDebugHandler(team_id team)
401 	:
402 	BLocker("team debug handler"),
403 	fMessages(),
404 	fMessageCountSem(-1),
405 	fTeam(team),
406 	fHandlerThread(-1)
407 {
408 	fDebugContext.nub_port = -1;
409 	fDebugContext.reply_port = -1;
410 
411 	fExecutablePath[0] = '\0';
412 }
413 
414 
415 TeamDebugHandler::~TeamDebugHandler()
416 {
417 	// delete the message count semaphore and wait for the thread to die
418 	if (fMessageCountSem >= 0)
419 		delete_sem(fMessageCountSem);
420 
421 	if (fHandlerThread >= 0 && find_thread(NULL) != fHandlerThread) {
422 		status_t result;
423 		wait_for_thread(fHandlerThread, &result);
424 	}
425 
426 	// destroy debug context
427 	if (fDebugContext.nub_port >= 0)
428 		destroy_debug_context(&fDebugContext);
429 
430 	// delete the remaining messages
431 	while (DebugMessage *message = fMessages.Head()) {
432 		fMessages.Remove(message);
433 		delete message;
434 	}
435 }
436 
437 
438 status_t
439 TeamDebugHandler::Init(port_id nubPort)
440 {
441 	// get the team info for the team
442 	status_t error = get_team_info(fTeam, &fTeamInfo);
443 	if (error != B_OK) {
444 		debug_printf("debug_server: TeamDebugHandler::Init(): Failed to get "
445 			"info for team %" B_PRId32 ": %s\n", fTeam, strerror(error));
446 		return error;
447 	}
448 
449 	// get the executable path
450 	error = BPrivate::get_app_path(fTeam, fExecutablePath);
451 	if (error != B_OK) {
452 		debug_printf("debug_server: TeamDebugHandler::Init(): Failed to get "
453 			"executable path of team %" B_PRId32 ": %s\n", fTeam,
454 			strerror(error));
455 
456 		fExecutablePath[0] = '\0';
457 	}
458 
459 	// init a debug context for the handler
460 	error = init_debug_context(&fDebugContext, fTeam, nubPort);
461 	if (error != B_OK) {
462 		debug_printf("debug_server: TeamDebugHandler::Init(): Failed to init "
463 			"debug context for team %" B_PRId32 ", port %" B_PRId32 ": %s\n",
464 			fTeam, nubPort, strerror(error));
465 		return error;
466 	}
467 
468 	// set team flags
469 	debug_nub_set_team_flags message;
470 	message.flags = B_TEAM_DEBUG_PREVENT_EXIT;
471 
472 	send_debug_message(&fDebugContext, B_DEBUG_MESSAGE_SET_TEAM_FLAGS, &message,
473 		sizeof(message), NULL, 0);
474 
475 	// create the message count semaphore
476 	char name[B_OS_NAME_LENGTH];
477 	snprintf(name, sizeof(name), "team %" B_PRId32 " message count", fTeam);
478 	fMessageCountSem = create_sem(0, name);
479 	if (fMessageCountSem < 0) {
480 		debug_printf("debug_server: TeamDebugHandler::Init(): Failed to create "
481 			"message count semaphore: %s\n", strerror(fMessageCountSem));
482 		return fMessageCountSem;
483 	}
484 
485 	// spawn the handler thread
486 	snprintf(name, sizeof(name), "team %" B_PRId32 " handler", fTeam);
487 	fHandlerThread = spawn_thread(&_HandlerThreadEntry, name, B_NORMAL_PRIORITY,
488 		this);
489 	if (fHandlerThread < 0) {
490 		debug_printf("debug_server: TeamDebugHandler::Init(): Failed to spawn "
491 			"handler thread: %s\n", strerror(fHandlerThread));
492 		return fHandlerThread;
493 	}
494 
495 	resume_thread(fHandlerThread);
496 
497 	return B_OK;
498 }
499 
500 
501 team_id
502 TeamDebugHandler::Team() const
503 {
504 	return fTeam;
505 }
506 
507 
508 status_t
509 TeamDebugHandler::PushMessage(DebugMessage *message)
510 {
511 	BAutolock _(this);
512 
513 	fMessages.Add(message);
514 	release_sem(fMessageCountSem);
515 
516 	return B_OK;
517 }
518 
519 
520 status_t
521 TeamDebugHandler::_PopMessage(DebugMessage *&message)
522 {
523 	// acquire the semaphore
524 	status_t error;
525 	do {
526 		error = acquire_sem(fMessageCountSem);
527 	} while (error == B_INTERRUPTED);
528 
529 	if (error != B_OK)
530 		return error;
531 
532 	// get the message
533 	BAutolock _(this);
534 
535 	message = fMessages.Head();
536 	fMessages.Remove(message);
537 
538 	return B_OK;
539 }
540 
541 
542 status_t
543 TeamDebugHandler::_SetupGDBArguments(BStringList &arguments, bool usingConsoled)
544 {
545 	// prepare the argument vector
546 	BString teamString;
547 	teamString.SetToFormat("--pid=%" B_PRId32, fTeam);
548 
549 	status_t error;
550 	BPath terminalPath;
551 	if (usingConsoled) {
552 		error = find_directory(B_SYSTEM_BIN_DIRECTORY, &terminalPath);
553 		if (error != B_OK) {
554 			debug_printf("debug_server: can't find system-bin directory: %s\n",
555 				strerror(error));
556 			return error;
557 		}
558 		error = terminalPath.Append("consoled");
559 		if (error != B_OK) {
560 			debug_printf("debug_server: can't append to system-bin path: %s\n",
561 				strerror(error));
562 			return error;
563 		}
564 	} else {
565 		error = find_directory(B_SYSTEM_APPS_DIRECTORY, &terminalPath);
566 		if (error != B_OK) {
567 			debug_printf("debug_server: can't find system-apps directory: %s\n",
568 				strerror(error));
569 			return error;
570 		}
571 		error = terminalPath.Append("Terminal");
572 		if (error != B_OK) {
573 			debug_printf("debug_server: can't append to system-apps path: %s\n",
574 				strerror(error));
575 			return error;
576 		}
577 	}
578 
579 	arguments.MakeEmpty();
580 	if (!arguments.Add(terminalPath.Path()))
581 		return B_NO_MEMORY;
582 
583 	if (!usingConsoled) {
584 		BString windowTitle;
585 		windowTitle.SetToFormat("Debug of Team %" B_PRId32 ": %s", fTeam,
586 			_LastPathComponent(fExecutablePath));
587 		if (!arguments.Add("-t") || !arguments.Add(windowTitle))
588 			return B_NO_MEMORY;
589 	}
590 
591 	BPath gdbPath;
592 	error = find_directory(B_SYSTEM_BIN_DIRECTORY, &gdbPath);
593 	if (error != B_OK) {
594 		debug_printf("debug_server: can't find system-bin directory: %s\n",
595 			strerror(error));
596 		return error;
597 	}
598 	error = gdbPath.Append("gdb");
599 	if (error != B_OK) {
600 		debug_printf("debug_server: can't append to system-bin path: %s\n",
601 			strerror(error));
602 		return error;
603 	}
604 	if (!arguments.Add(gdbPath.Path()) || !arguments.Add(teamString))
605 		return B_NO_MEMORY;
606 
607 	if (strlen(fExecutablePath) > 0 && !arguments.Add(fExecutablePath))
608 		return B_NO_MEMORY;
609 
610 	return B_OK;
611 }
612 
613 
614 thread_id
615 TeamDebugHandler::_EnterDebugger(bool saveReport)
616 {
617 	TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): team %" B_PRId32
618 		"\n", fTeam));
619 
620 	// prepare a debugger handover
621 	TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): preparing "
622 		"debugger handover for team %" B_PRId32 "...\n", fTeam));
623 
624 	status_t error = send_debug_message(&fDebugContext,
625 		B_DEBUG_MESSAGE_PREPARE_HANDOVER, NULL, 0, NULL, 0);
626 	if (error != B_OK) {
627 		debug_printf("debug_server: Failed to prepare debugger handover: %s\n",
628 			strerror(error));
629 		return error;
630 	}
631 
632 	BStringList arguments;
633 	const char *argv[16];
634 	int argc = 0;
635 
636 	bool debugInConsoled = _IsGUIServer() || !_AreGUIServersAlive();
637 #ifdef HANDOVER_USE_GDB
638 
639 	error = _SetupGDBArguments(arguments, debugInConsoled);
640 	if (error != B_OK) {
641 		debug_printf("debug_server: Failed to set up gdb arguments: %s\n",
642 			strerror(error));
643 		return error;
644 	}
645 
646 	// start the terminal
647 	TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): starting  "
648 		"terminal (debugger) for team %" B_PRId32 "...\n", fTeam));
649 
650 #elif defined(HANDOVER_USE_DEBUGGER)
651 	// prepare the argument vector
652 	BPath debuggerPath;
653 	if (debugInConsoled) {
654 		error = find_directory(B_SYSTEM_BIN_DIRECTORY, &debuggerPath);
655 		if (error != B_OK) {
656 			debug_printf("debug_server: can't find system-bin directory: %s\n",
657 				strerror(error));
658 			return error;
659 		}
660 		error = debuggerPath.Append("consoled");
661 		if (error != B_OK) {
662 			debug_printf("debug_server: can't append to system-bin path: %s\n",
663 				strerror(error));
664 			return error;
665 		}
666 
667 		if (!arguments.Add(debuggerPath.Path()))
668 			return B_NO_MEMORY;
669 	}
670 
671 	error = find_directory(B_SYSTEM_APPS_DIRECTORY, &debuggerPath);
672 	if (error != B_OK) {
673 		debug_printf("debug_server: can't find system-apps directory: %s\n",
674 			strerror(error));
675 		return error;
676 	}
677 	error = debuggerPath.Append("Debugger");
678 	if (error != B_OK) {
679 		debug_printf("debug_server: can't append to system-apps path: %s\n",
680 			strerror(error));
681 		return error;
682 	}
683 	if (!arguments.Add(debuggerPath.Path()))
684 		return B_NO_MEMORY;
685 
686 	if (debugInConsoled && !arguments.Add("--cli"))
687 		return B_NO_MEMORY;
688 
689 	BString debuggerParam;
690 	debuggerParam.SetToFormat("%" B_PRId32, fTeam);
691 	if (saveReport) {
692 		if (!arguments.Add("--save-report"))
693 			return B_NO_MEMORY;
694 	}
695 	if (!arguments.Add("--team") || !arguments.Add(debuggerParam))
696 		return B_NO_MEMORY;
697 
698 	// start the debugger
699 	TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): starting  "
700 		"%s debugger for team %" B_PRId32 "...\n",
701 			debugInConsoled ? "command line" : "graphical", fTeam));
702 #endif
703 
704 	for (int32 i = 0; i < arguments.CountStrings(); i++)
705 		argv[argc++] = arguments.StringAt(i).String();
706 	argv[argc] = NULL;
707 
708 	thread_id thread = load_image(argc, argv, (const char**)environ);
709 	if (thread < 0) {
710 		debug_printf("debug_server: Failed to start debugger: %s\n",
711 			strerror(thread));
712 		return thread;
713 	}
714 	resume_thread(thread);
715 
716 	TRACE(("debug_server: TeamDebugHandler::_EnterDebugger(): debugger started "
717 		"for team %" B_PRId32 ": thread: %" B_PRId32 "\n", fTeam, thread));
718 
719 	return thread;
720 }
721 
722 
723 void
724 TeamDebugHandler::_KillTeam()
725 {
726 	KillTeam(fTeam, fTeamInfo.args);
727 }
728 
729 
730 int32
731 TeamDebugHandler::_HandleMessage(DebugMessage *message)
732 {
733 	// This method is called only for the first message the debugger gets for
734 	// a team. That means only a few messages are actually possible, while
735 	// others wouldn't trigger the debugger in the first place. So we deal with
736 	// all of them the same way, by popping up an alert.
737 	TRACE(("debug_server: TeamDebugHandler::_HandleMessage(): team %" B_PRId32
738 		", code: %" B_PRId32 "\n", fTeam, (int32)message->Code()));
739 
740 	thread_id thread = message->Data().origin.thread;
741 
742 	// get some user-readable message
743 	char buffer[512];
744 	switch (message->Code()) {
745 		case B_DEBUGGER_MESSAGE_TEAM_DELETED:
746 			// This shouldn't happen.
747 			debug_printf("debug_server: Got a spurious "
748 				"B_DEBUGGER_MESSAGE_TEAM_DELETED message for team %" B_PRId32
749 				"\n", fTeam);
750 			return true;
751 
752 		case B_DEBUGGER_MESSAGE_EXCEPTION_OCCURRED:
753 			get_debug_exception_string(
754 				message->Data().exception_occurred.exception, buffer,
755 				sizeof(buffer));
756 			break;
757 
758 		case B_DEBUGGER_MESSAGE_DEBUGGER_CALL:
759 		{
760 			// get the debugger() message
761 			void *messageAddress = message->Data().debugger_call.message;
762 			char messageBuffer[128];
763 			status_t error = B_OK;
764 			ssize_t bytesRead = debug_read_string(&fDebugContext,
765 				messageAddress, messageBuffer, sizeof(messageBuffer));
766 			if (bytesRead < 0)
767 				error = bytesRead;
768 
769 			if (error == B_OK) {
770 				sprintf(buffer, "Debugger call: `%s'", messageBuffer);
771 			} else {
772 				snprintf(buffer, sizeof(buffer), "Debugger call: %p "
773 					"(Failed to read message: %s)", messageAddress,
774 					strerror(error));
775 			}
776 			break;
777 		}
778 
779 		default:
780 			get_debug_message_string(message->Code(), buffer, sizeof(buffer));
781 			break;
782 	}
783 
784 	debug_printf("debug_server: Thread %" B_PRId32 " entered the debugger: %s\n",
785 		thread, buffer);
786 
787 	_PrintStackTrace(thread);
788 
789 	int32 debugAction = kActionPromptUser;
790 	bool explicitActionFound = false;
791 	if (action_for_team(fExecutablePath, debugAction, explicitActionFound)
792 			!= B_OK) {
793 		debugAction = kActionPromptUser;
794 		explicitActionFound = false;
795 	}
796 
797 	// ask the user whether to debug or kill the team
798 	if (_IsGUIServer()) {
799 		// App server, input server, or registrar. We always debug those.
800 		// if not specifically overridden.
801 		if (!explicitActionFound)
802 			debugAction = kActionDebugTeam;
803 	} else if (debugAction == kActionPromptUser && USE_GUI
804 		&& _AreGUIServersAlive() && _InitGUI() == B_OK) {
805 		// normal app -- tell the user
806 		_NotifyAppServer(fTeam);
807 		_NotifyRegistrar(fTeam, true, false);
808 
809 		BString buffer(
810 			B_TRANSLATE("The application:\n\n      %app\n\n"
811 			"has encountered an error which prevents it from continuing. Haiku "
812 			"will terminate the application and clean up."));
813 		buffer.ReplaceFirst("%app", fTeamInfo.args);
814 
815 		// TODO: It would be nice if the alert would go away automatically
816 		// if someone else kills our teams.
817 #ifdef HANDOVER_USE_DEBUGGER
818 		BAlert *alert = new BAlert(NULL, buffer.String(),
819 			B_TRANSLATE("Terminate"), B_TRANSLATE("Debug"),
820 			B_TRANSLATE("Save report"), B_WIDTH_AS_USUAL, B_WARNING_ALERT);
821 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
822 		debugAction = alert->Go();
823 		_NotifyRegistrar(fTeam, false, debugAction != kActionKillTeam);
824 #else
825 		BAlert *alert = new BAlert(NULL, buffer.String(),
826 			B_TRANSLATE("Kill"), B_TRANSLATE("Debug"), NULL,
827 			B_WIDTH_AS_USUAL, B_WARNING_ALERT);
828 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
829 		debugAction = alert->Go();
830 		_NotifyRegistrar(fTeam, false, debugAction != kActionKillTeam);
831 #endif
832 	}
833 
834 	return debugAction;
835 }
836 
837 
838 void
839 TeamDebugHandler::_LookupSymbolAddress(
840 	debug_symbol_lookup_context *lookupContext, const void *address,
841 	char *buffer, int32 bufferSize)
842 {
843 	// lookup the symbol
844 	void *baseAddress;
845 	char symbolName[1024];
846 	char imageName[B_PATH_NAME_LENGTH];
847 	bool exactMatch;
848 	bool lookupSucceeded = false;
849 	if (lookupContext) {
850 		status_t error = debug_lookup_symbol_address(lookupContext, address,
851 			&baseAddress, symbolName, sizeof(symbolName), imageName,
852 			sizeof(imageName), &exactMatch);
853 		lookupSucceeded = (error == B_OK);
854 	}
855 
856 	if (lookupSucceeded) {
857 		// we were able to look something up
858 		if (strlen(symbolName) > 0) {
859 			// we even got a symbol
860 			snprintf(buffer, bufferSize, "%s + %#lx%s", symbolName,
861 				(addr_t)address - (addr_t)baseAddress,
862 				(exactMatch ? "" : " (closest symbol)"));
863 
864 		} else {
865 			// no symbol: image relative address
866 			snprintf(buffer, bufferSize, "(%s + %#lx)", imageName,
867 				(addr_t)address - (addr_t)baseAddress);
868 		}
869 
870 	} else {
871 		// lookup failed: find area containing the IP
872 		bool useAreaInfo = false;
873 		area_info info;
874 		ssize_t cookie = 0;
875 		while (get_next_area_info(fTeam, &cookie, &info) == B_OK) {
876 			if ((addr_t)info.address <= (addr_t)address
877 				&& (addr_t)info.address + info.size > (addr_t)address) {
878 				useAreaInfo = true;
879 				break;
880 			}
881 		}
882 
883 		if (useAreaInfo) {
884 			snprintf(buffer, bufferSize, "(%s + %#lx)", info.name,
885 				(addr_t)address - (addr_t)info.address);
886 		} else if (bufferSize > 0)
887 			buffer[0] = '\0';
888 	}
889 }
890 
891 
892 void
893 TeamDebugHandler::_PrintStackTrace(thread_id thread)
894 {
895 	// print a stacktrace
896 	void *ip = NULL;
897 	void *stackFrameAddress = NULL;
898 	status_t error = debug_get_instruction_pointer(&fDebugContext, thread, &ip,
899 		&stackFrameAddress);
900 
901 	if (error == B_OK) {
902 		// create a symbol lookup context
903 		debug_symbol_lookup_context *lookupContext = NULL;
904 		error = debug_create_symbol_lookup_context(fTeam, -1, &lookupContext);
905 		if (error != B_OK) {
906 			debug_printf("debug_server: Failed to create symbol lookup "
907 				"context: %s\n", strerror(error));
908 		}
909 
910 		// lookup the IP
911 		char symbolBuffer[2048];
912 		_LookupSymbolAddress(lookupContext, ip, symbolBuffer,
913 			sizeof(symbolBuffer) - 1);
914 
915 		debug_printf("stack trace, current PC %p  %s:\n", ip, symbolBuffer);
916 
917 		for (int32 i = 0; i < 50; i++) {
918 			debug_stack_frame_info stackFrameInfo;
919 
920 			error = debug_get_stack_frame(&fDebugContext, stackFrameAddress,
921 				&stackFrameInfo);
922 			if (error < B_OK || stackFrameInfo.parent_frame == NULL)
923 				break;
924 
925 			// lookup the return address
926 			_LookupSymbolAddress(lookupContext, stackFrameInfo.return_address,
927 				symbolBuffer, sizeof(symbolBuffer) - 1);
928 
929 			debug_printf("  (%p)  %p  %s\n", stackFrameInfo.frame,
930 				stackFrameInfo.return_address, symbolBuffer);
931 
932 			stackFrameAddress = stackFrameInfo.parent_frame;
933 		}
934 
935 		// delete the symbol lookup context
936 		if (lookupContext)
937 			debug_delete_symbol_lookup_context(lookupContext);
938 	}
939 }
940 
941 
942 void
943 TeamDebugHandler::_NotifyAppServer(team_id team)
944 {
945 	// This will remove any kWindowScreenFeels of the application, so that
946 	// the debugger alert is visible on screen
947 	BRoster::Private roster;
948 	roster.ApplicationCrashed(team);
949 }
950 
951 
952 void
953 TeamDebugHandler::_NotifyRegistrar(team_id team, bool openAlert,
954 	bool stopShutdown)
955 {
956 	BMessage notify(BPrivate::B_REG_TEAM_DEBUGGER_ALERT);
957 	notify.AddInt32("team", team);
958 	notify.AddBool("open", openAlert);
959 	notify.AddBool("stop shutdown", stopShutdown);
960 
961 	BRoster::Private roster;
962 	BMessage reply;
963 	roster.SendTo(&notify, &reply, false);
964 }
965 
966 
967 status_t
968 TeamDebugHandler::_InitGUI()
969 {
970 	DebugServer *app = dynamic_cast<DebugServer*>(be_app);
971 	BAutolock _(app);
972 	return app->InitGUIContext();
973 }
974 
975 
976 status_t
977 TeamDebugHandler::_HandlerThreadEntry(void *data)
978 {
979 	return ((TeamDebugHandler*)data)->_HandlerThread();
980 }
981 
982 
983 status_t
984 TeamDebugHandler::_HandlerThread()
985 {
986 	TRACE(("debug_server: TeamDebugHandler::_HandlerThread(): team %" B_PRId32
987 		"\n", fTeam));
988 
989 	// get initial message
990 	TRACE(("debug_server: TeamDebugHandler::_HandlerThread(): team %" B_PRId32
991 		": getting message...\n", fTeam));
992 
993 	DebugMessage *message;
994 	status_t error = _PopMessage(message);
995 	int32 debugAction = kActionKillTeam;
996 	if (error == B_OK) {
997 		// handle the message
998 		debugAction = _HandleMessage(message);
999 		delete message;
1000 	} else {
1001 		debug_printf("TeamDebugHandler::_HandlerThread(): Failed to pop "
1002 			"initial message: %s", strerror(error));
1003 	}
1004 
1005 	bool isGuiServer = _IsGUIServer();
1006 
1007 	// kill the team or hand it over to the debugger
1008 	thread_id debuggerThread = -1;
1009 	if (debugAction == kActionKillTeam) {
1010 		// The team shall be killed. Since that is also the handling in case
1011 		// an error occurs while handing over the team to the debugger, we do
1012 		// nothing here.
1013 	} else if ((debuggerThread = _EnterDebugger(
1014 			debugAction == kActionSaveReportTeam)) >= 0) {
1015 		// wait for the "handed over" or a "team deleted" message
1016 		bool terminate = false;
1017 		do {
1018 			error = _PopMessage(message);
1019 			if (error != B_OK) {
1020 				debug_printf("TeamDebugHandler::_HandlerThread(): Failed to "
1021 					"pop message: %s", strerror(error));
1022 				debugAction = kActionKillTeam;
1023 				break;
1024 			}
1025 
1026 			if (message->Code() == B_DEBUGGER_MESSAGE_HANDED_OVER) {
1027 				// The team has successfully been handed over to the debugger.
1028 				// Nothing to do.
1029 				terminate = true;
1030 			} else if (message->Code() == B_DEBUGGER_MESSAGE_TEAM_DELETED) {
1031 				// The team died. Nothing to do.
1032 				terminate = true;
1033 			} else {
1034 				// Some message we can ignore. The debugger will take care of
1035 				// it.
1036 
1037 				// check whether the debugger thread still lives
1038 				thread_info threadInfo;
1039 				if (get_thread_info(debuggerThread, &threadInfo) != B_OK) {
1040 					// the debugger is gone
1041 					debug_printf("debug_server: The debugger for team %"
1042 						B_PRId32 " seems to be gone.", fTeam);
1043 
1044 					debugAction = kActionKillTeam;
1045 					terminate = true;
1046 				}
1047 			}
1048 
1049 			delete message;
1050 		} while (!terminate);
1051 	} else
1052 		debugAction = kActionKillTeam;
1053 
1054 	if (debugAction == kActionKillTeam) {
1055 		// kill the team
1056 		_KillTeam();
1057 	}
1058 
1059 	// remove this handler from the roster and delete it
1060 	TeamDebugHandlerRoster::Default()->RemoveHandler(fTeam);
1061 
1062 	if (isGuiServer) {
1063 		// wait till debugging is done
1064 		status_t dummy;
1065 		wait_for_thread(debuggerThread, &dummy);
1066 
1067 		// find the registrar port
1068 		port_id rosterPort = find_port(BPrivate::get_roster_port_name());
1069 		port_info info;
1070 		BMessenger messenger;
1071 		if (rosterPort >= 0 && get_port_info(rosterPort, &info) == B_OK) {
1072 			BMessenger::Private(messenger).SetTo(info.team, rosterPort,
1073 				B_PREFERRED_TOKEN);
1074 		}
1075 		messenger.SendMessage(kMsgRestartAppServer);
1076 	}
1077 
1078 	delete this;
1079 
1080 	return B_OK;
1081 }
1082 
1083 
1084 bool
1085 TeamDebugHandler::_ExecutableNameEquals(const char *name) const
1086 {
1087 	return strcmp(_LastPathComponent(fExecutablePath), name) == 0;
1088 }
1089 
1090 
1091 bool
1092 TeamDebugHandler::_IsAppServer() const
1093 {
1094 	return _ExecutableNameEquals("app_server");
1095 }
1096 
1097 
1098 bool
1099 TeamDebugHandler::_IsInputServer() const
1100 {
1101 	return _ExecutableNameEquals("input_server");
1102 }
1103 
1104 
1105 bool
1106 TeamDebugHandler::_IsRegistrar() const
1107 {
1108 	return _ExecutableNameEquals("registrar");
1109 }
1110 
1111 
1112 bool
1113 TeamDebugHandler::_IsGUIServer() const
1114 {
1115 	// app or input server
1116 	return _IsAppServer() || _IsInputServer() || _IsRegistrar();
1117 }
1118 
1119 
1120 const char *
1121 TeamDebugHandler::_LastPathComponent(const char *path)
1122 {
1123 	const char *lastSlash = strrchr(path, '/');
1124 	return lastSlash ? lastSlash + 1 : path;
1125 }
1126 
1127 
1128 team_id
1129 TeamDebugHandler::_FindTeam(const char *name)
1130 {
1131 	// Iterate through all teams and check their executable name.
1132 	int32 cookie = 0;
1133 	team_info teamInfo;
1134 	while (get_next_team_info(&cookie, &teamInfo) == B_OK) {
1135 		entry_ref ref;
1136 		if (BPrivate::get_app_ref(teamInfo.team, &ref) == B_OK) {
1137 			if (strcmp(ref.name, name) == 0)
1138 				return teamInfo.team;
1139 		}
1140 	}
1141 
1142 	return B_ENTRY_NOT_FOUND;
1143 }
1144 
1145 
1146 bool
1147 TeamDebugHandler::_AreGUIServersAlive()
1148 {
1149 	return _FindTeam("app_server") >= 0 && _FindTeam("input_server") >= 0
1150 		&& _FindTeam("registrar");
1151 }
1152 
1153 
1154 // #pragma mark -
1155 
1156 
1157 DebugServer::DebugServer(status_t &error)
1158 	:
1159 	BServer(kSignature, false, &error),
1160 	fListenerPort(-1),
1161 	fListener(-1),
1162 	fTerminating(false)
1163 {
1164 }
1165 
1166 
1167 status_t
1168 DebugServer::Init()
1169 {
1170 	// create listener port
1171 	fListenerPort = create_port(10, "kernel listener");
1172 	if (fListenerPort < 0)
1173 		return fListenerPort;
1174 
1175 	// spawn the listener thread
1176 	fListener = spawn_thread(_ListenerEntry, "kernel listener",
1177 		B_NORMAL_PRIORITY, this);
1178 	if (fListener < 0)
1179 		return fListener;
1180 
1181 	// register as default debugger
1182 	// TODO: could set default flags
1183 	status_t error = install_default_debugger(fListenerPort);
1184 	if (error != B_OK)
1185 		return error;
1186 
1187 	// resume the listener
1188 	resume_thread(fListener);
1189 
1190 	return B_OK;
1191 }
1192 
1193 
1194 bool
1195 DebugServer::QuitRequested()
1196 {
1197 	// Never give up, never surrender. ;-)
1198 	return false;
1199 }
1200 
1201 
1202 status_t
1203 DebugServer::_ListenerEntry(void *data)
1204 {
1205 	return ((DebugServer*)data)->_Listener();
1206 }
1207 
1208 
1209 status_t
1210 DebugServer::_Listener()
1211 {
1212 	while (!fTerminating) {
1213 		// receive the next debug message
1214 		DebugMessage *message = new DebugMessage;
1215 		int32 code;
1216 		ssize_t bytesRead;
1217 		do {
1218 			bytesRead = read_port(fListenerPort, &code, &message->Data(),
1219 				sizeof(debug_debugger_message_data));
1220 		} while (bytesRead == B_INTERRUPTED);
1221 
1222 		if (bytesRead < 0) {
1223 			debug_printf("debug_server: Failed to read from listener port: "
1224 				"%s. Terminating!\n", strerror(bytesRead));
1225 			exit(1);
1226 		}
1227 TRACE(("debug_server: Got debug message: team: %" B_PRId32 ", code: %" B_PRId32
1228 	"\n", message->Data().origin.team, code));
1229 
1230 		message->SetCode((debug_debugger_message)code);
1231 
1232 		// dispatch the message
1233 		TeamDebugHandlerRoster::Default()->DispatchMessage(message);
1234 	}
1235 
1236 	return B_OK;
1237 }
1238 
1239 
1240 // #pragma mark -
1241 
1242 
1243 int
1244 main()
1245 {
1246 	status_t error;
1247 
1248 	// for the time being let the debug server print to the syslog
1249 	int console = open("/dev/dprintf", O_RDONLY);
1250 	if (console < 0) {
1251 		debug_printf("debug_server: Failed to open console: %s\n",
1252 			strerror(errno));
1253 	}
1254 	dup2(console, STDOUT_FILENO);
1255 	dup2(console, STDERR_FILENO);
1256 	close(console);
1257 
1258 	// create the team debug handler roster
1259 	if (!TeamDebugHandlerRoster::CreateDefault()) {
1260 		debug_printf("debug_server: Failed to create team debug handler "
1261 			"roster.\n");
1262 		exit(1);
1263 	}
1264 
1265 	// create application
1266 	DebugServer server(error);
1267 	if (error != B_OK) {
1268 		debug_printf("debug_server: Failed to create BApplication: %s\n",
1269 			strerror(error));
1270 		exit(1);
1271 	}
1272 
1273 	// init application
1274 	error = server.Init();
1275 	if (error != B_OK) {
1276 		debug_printf("debug_server: Failed to init application: %s\n",
1277 			strerror(error));
1278 		exit(1);
1279 	}
1280 
1281 	server.Run();
1282 
1283 	return 0;
1284 }
1285