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