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