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
37enum {
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
61using std::map;
62using std::nothrow;
63
64
65static const char *kSignature = "application/x-vnd.Haiku-debug_server";
66
67
68static void
69KillTeam(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
94class DebugMessage : public DoublyLinkedListLinkImpl<DebugMessage> {
95public:
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
106private:
107	debug_debugger_message		fCode;
108	debug_debugger_message_data	fData;
109};
110
111typedef DoublyLinkedList<DebugMessage>	DebugMessageList;
112
113
114class TeamDebugHandler : public BLocker {
115public:
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
125private:
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
155private:
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
166class TeamDebugHandlerRoster : public BLocker {
167private:
168	TeamDebugHandlerRoster()
169		:
170		BLocker("team debug handler roster")
171	{
172	}
173
174public:
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
270private:
271	typedef map<team_id, TeamDebugHandler*>	TeamDebugHandlerMap;
272
273	static TeamDebugHandlerRoster	*sRoster;
274
275	TeamDebugHandlerMap				fHandlers;
276};
277
278TeamDebugHandlerRoster *TeamDebugHandlerRoster::sRoster = NULL;
279
280
281class DebugServer : public BServer {
282public:
283	DebugServer(status_t &error);
284
285	status_t Init();
286
287	virtual bool QuitRequested();
288
289private:
290	static status_t _ListenerEntry(void *data);
291	status_t _Listener();
292
293	void _DeleteTeamDebugHandler(TeamDebugHandler *handler);
294
295private:
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
307TeamDebugHandler::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
322TeamDebugHandler::~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
345status_t
346TeamDebugHandler::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
408team_id
409TeamDebugHandler::Team() const
410{
411	return fTeam;
412}
413
414
415status_t
416TeamDebugHandler::PushMessage(DebugMessage *message)
417{
418	BAutolock _(this);
419
420	fMessages.Add(message);
421	release_sem(fMessageCountSem);
422
423	return B_OK;
424}
425
426
427status_t
428TeamDebugHandler::_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
449status_t
450TeamDebugHandler::_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
521thread_id
522TeamDebugHandler::_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
630void
631TeamDebugHandler::_KillTeam()
632{
633	KillTeam(fTeam, fTeamInfo.args);
634}
635
636
637int32
638TeamDebugHandler::_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
736void
737TeamDebugHandler::_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
790void
791TeamDebugHandler::_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, &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
840void
841TeamDebugHandler::_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
850void
851TeamDebugHandler::_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
865status_t
866TeamDebugHandler::_InitGUI()
867{
868	DebugServer *app = dynamic_cast<DebugServer*>(be_app);
869	BAutolock _(app);
870	return app->InitGUIContext();
871}
872
873
874status_t
875TeamDebugHandler::_HandlerThreadEntry(void *data)
876{
877	return ((TeamDebugHandler*)data)->_HandlerThread();
878}
879
880
881status_t
882TeamDebugHandler::_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
982bool
983TeamDebugHandler::_ExecutableNameEquals(const char *name) const
984{
985	return strcmp(_LastPathComponent(fExecutablePath), name) == 0;
986}
987
988
989bool
990TeamDebugHandler::_IsAppServer() const
991{
992	return _ExecutableNameEquals("app_server");
993}
994
995
996bool
997TeamDebugHandler::_IsInputServer() const
998{
999	return _ExecutableNameEquals("input_server");
1000}
1001
1002
1003bool
1004TeamDebugHandler::_IsRegistrar() const
1005{
1006	return _ExecutableNameEquals("registrar");
1007}
1008
1009
1010bool
1011TeamDebugHandler::_IsGUIServer() const
1012{
1013	// app or input server
1014	return _IsAppServer() || _IsInputServer() || _IsRegistrar();
1015}
1016
1017
1018const char *
1019TeamDebugHandler::_LastPathComponent(const char *path)
1020{
1021	const char *lastSlash = strrchr(path, '/');
1022	return lastSlash ? lastSlash + 1 : path;
1023}
1024
1025
1026team_id
1027TeamDebugHandler::_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
1044bool
1045TeamDebugHandler::_AreGUIServersAlive()
1046{
1047	return _FindTeam("app_server") >= 0 && _FindTeam("input_server") >= 0
1048		&& _FindTeam("registrar");
1049}
1050
1051
1052// #pragma mark -
1053
1054
1055DebugServer::DebugServer(status_t &error)
1056	:
1057	BServer(kSignature, false, &error),
1058	fListenerPort(-1),
1059	fListener(-1),
1060	fTerminating(false)
1061{
1062}
1063
1064
1065status_t
1066DebugServer::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
1092bool
1093DebugServer::QuitRequested()
1094{
1095	// Never give up, never surrender. ;-)
1096	return false;
1097}
1098
1099
1100status_t
1101DebugServer::_ListenerEntry(void *data)
1102{
1103	return ((DebugServer*)data)->_Listener();
1104}
1105
1106
1107status_t
1108DebugServer::_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		}
1125TRACE(("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
1141int
1142main()
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