xref: /haiku/src/servers/debug/DebugServer.cpp (revision 36fa4b7e7dbed60f4cf051d8c8ae6fe3cd13b75e)
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 
39 static const char* kDebuggerSignature = "application/x-vnd.Haiku-Debugger";
40 static 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 
51 using std::map;
52 using std::nothrow;
53 
54 
55 static const char *kSignature = "application/x-vnd.Haiku-debug_server";
56 
57 
58 static status_t
action_for_string(const char * action,int32 & _action)59 action_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 
79 static bool
match_team_name(const char * teamName,const char * parameterName)80 match_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 
103 static status_t
action_for_team(const char * teamName,int32 & _action,bool & _explicitActionFound)104 action_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 
148 static void
KillTeam(team_id team,const char * appName=NULL)149 KillTeam(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 
174 class DebugMessage : public DoublyLinkedListLinkImpl<DebugMessage> {
175 public:
DebugMessage()176 	DebugMessage()
177 	{
178 	}
179 
SetCode(debug_debugger_message code)180 	void SetCode(debug_debugger_message code)		{ fCode = code; }
Code() const181 	debug_debugger_message Code() const				{ return fCode; }
182 
Data()183 	debug_debugger_message_data &Data()				{ return fData; }
Data() const184 	const debug_debugger_message_data &Data() const	{ return fData; }
185 
186 private:
187 	debug_debugger_message		fCode;
188 	debug_debugger_message_data	fData;
189 };
190 
191 typedef DoublyLinkedList<DebugMessage>	DebugMessageList;
192 
193 
194 class TeamDebugHandler : public BLocker {
195 public:
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 
205 private:
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 
236 private:
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 
247 class TeamDebugHandlerRoster : public BLocker {
248 private:
TeamDebugHandlerRoster()249 	TeamDebugHandlerRoster()
250 		:
251 		BLocker("team debug handler roster")
252 	{
253 	}
254 
255 public:
CreateDefault()256 	static TeamDebugHandlerRoster *CreateDefault()
257 	{
258 		if (!sRoster)
259 			sRoster = new(nothrow) TeamDebugHandlerRoster;
260 
261 		return sRoster;
262 	}
263 
Default()264 	static TeamDebugHandlerRoster *Default()
265 	{
266 		return sRoster;
267 	}
268 
AddHandler(TeamDebugHandler * handler)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 
RemoveHandler(team_id team)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 
HandlerFor(team_id team)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 
DispatchMessage(DebugMessage * message)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 
351 private:
352 	typedef map<team_id, TeamDebugHandler*>	TeamDebugHandlerMap;
353 
354 	static TeamDebugHandlerRoster	*sRoster;
355 
356 	TeamDebugHandlerMap				fHandlers;
357 };
358 
359 
360 TeamDebugHandlerRoster *TeamDebugHandlerRoster::sRoster = NULL;
361 
362 
363 class DebugServer : public BServer {
364 public:
365 	DebugServer(status_t &error);
366 
367 	status_t Init();
368 
369 	virtual bool QuitRequested();
370 
371 private:
372 	static status_t _ListenerEntry(void *data);
373 	status_t _Listener();
374 
375 	void _DeleteTeamDebugHandler(TeamDebugHandler *handler);
376 
377 private:
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 
TeamDebugHandler(team_id team)389 TeamDebugHandler::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 
~TeamDebugHandler()404 TeamDebugHandler::~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 
427 status_t
Init(port_id nubPort)428 TeamDebugHandler::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 
490 team_id
Team() const491 TeamDebugHandler::Team() const
492 {
493 	return fTeam;
494 }
495 
496 
497 status_t
PushMessage(DebugMessage * message)498 TeamDebugHandler::PushMessage(DebugMessage *message)
499 {
500 	BAutolock _(this);
501 
502 	fMessages.Add(message);
503 	release_sem(fMessageCountSem);
504 
505 	return B_OK;
506 }
507 
508 
509 status_t
_PopMessage(DebugMessage * & message)510 TeamDebugHandler::_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 
531 status_t
_SetupGDBArguments(BStringList & arguments,bool usingConsoled)532 TeamDebugHandler::_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 
603 thread_id
_EnterDebugger(bool saveReport)604 TeamDebugHandler::_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 
726 void
_KillTeam()727 TeamDebugHandler::_KillTeam()
728 {
729 	KillTeam(fTeam, fTeamInfo.args);
730 }
731 
732 
733 status_t
_WriteCoreFile()734 TeamDebugHandler::_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 
799 int32
_HandleMessage(DebugMessage * message)800 TeamDebugHandler::_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 
894 void
_LookupSymbolAddress(debug_symbol_lookup_context * lookupContext,const void * address,char * buffer,int32 bufferSize)895 TeamDebugHandler::_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 
948 void
_PrintStackTrace(thread_id thread)949 TeamDebugHandler::_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 
998 void
_NotifyAppServer(team_id team)999 TeamDebugHandler::_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 
1008 void
_NotifyRegistrar(team_id team,bool openAlert,bool stopShutdown)1009 TeamDebugHandler::_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 
1023 status_t
_InitGUI()1024 TeamDebugHandler::_InitGUI()
1025 {
1026 	DebugServer *app = dynamic_cast<DebugServer*>(be_app);
1027 	BAutolock _(app);
1028 	return app->InitGUIContext();
1029 }
1030 
1031 
1032 status_t
_HandlerThreadEntry(void * data)1033 TeamDebugHandler::_HandlerThreadEntry(void *data)
1034 {
1035 	return ((TeamDebugHandler*)data)->_HandlerThread();
1036 }
1037 
1038 
1039 status_t
_HandlerThread()1040 TeamDebugHandler::_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 
1125 bool
_ExecutableNameEquals(const char * name) const1126 TeamDebugHandler::_ExecutableNameEquals(const char *name) const
1127 {
1128 	return strcmp(_LastPathComponent(fExecutablePath), name) == 0;
1129 }
1130 
1131 
1132 bool
_IsAppServer() const1133 TeamDebugHandler::_IsAppServer() const
1134 {
1135 	return _ExecutableNameEquals("app_server");
1136 }
1137 
1138 
1139 bool
_IsInputServer() const1140 TeamDebugHandler::_IsInputServer() const
1141 {
1142 	return _ExecutableNameEquals("input_server");
1143 }
1144 
1145 
1146 bool
_IsRegistrar() const1147 TeamDebugHandler::_IsRegistrar() const
1148 {
1149 	return _ExecutableNameEquals("registrar");
1150 }
1151 
1152 
1153 bool
_IsGUIServer() const1154 TeamDebugHandler::_IsGUIServer() const
1155 {
1156 	// app or input server
1157 	return _IsAppServer() || _IsInputServer() || _IsRegistrar();
1158 }
1159 
1160 
1161 const char *
_LastPathComponent(const char * path)1162 TeamDebugHandler::_LastPathComponent(const char *path)
1163 {
1164 	const char *lastSlash = strrchr(path, '/');
1165 	return lastSlash ? lastSlash + 1 : path;
1166 }
1167 
1168 
1169 team_id
_FindTeam(const char * name)1170 TeamDebugHandler::_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 
1187 bool
_AreGUIServersAlive()1188 TeamDebugHandler::_AreGUIServersAlive()
1189 {
1190 	return _FindTeam("app_server") >= 0 && _FindTeam("input_server") >= 0
1191 		&& _FindTeam("registrar");
1192 }
1193 
1194 
1195 // #pragma mark -
1196 
1197 
DebugServer(status_t & error)1198 DebugServer::DebugServer(status_t &error)
1199 	:
1200 	BServer(kSignature, false, &error),
1201 	fListenerPort(-1),
1202 	fListener(-1),
1203 	fTerminating(false)
1204 {
1205 }
1206 
1207 
1208 status_t
Init()1209 DebugServer::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 
1235 bool
QuitRequested()1236 DebugServer::QuitRequested()
1237 {
1238 	// Never give up, never surrender. ;-)
1239 	return false;
1240 }
1241 
1242 
1243 status_t
_ListenerEntry(void * data)1244 DebugServer::_ListenerEntry(void *data)
1245 {
1246 	return ((DebugServer*)data)->_Listener();
1247 }
1248 
1249 
1250 status_t
_Listener()1251 DebugServer::_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 		}
1268 TRACE(("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 
1284 int
main()1285 main()
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