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