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