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(¬ify, &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