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