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