xref: /haiku/src/kits/debugger/target_host_interface/TargetHostInterface.cpp (revision 9642f7705b27e5c270c15fa526d14e1848c2c27d)
1 /*
2  * Copyright 2016, Rene Gollent, rene@gollent.com.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "TargetHostInterface.h"
7 
8 #include <stdio.h>
9 
10 #include <AutoLocker.h>
11 
12 #include "DebuggerInterface.h"
13 #include "MessageCodes.h"
14 #include "TeamDebugger.h"
15 
16 
17 // #pragma mark - TeamDebuggerOptions
18 
19 
20 TeamDebuggerOptions::TeamDebuggerOptions()
21 	:
22 	requestType(TEAM_DEBUGGER_REQUEST_UNKNOWN),
23 	commandLineArgc(0),
24 	commandLineArgv(NULL),
25 	team(-1),
26 	thread(-1),
27 	settingsManager(NULL),
28 	userInterface(NULL),
29 	coreFilePath(NULL)
30 {
31 }
32 
33 
34 // #pragma mark - TargetHostInterface
35 
36 
37 TargetHostInterface::TargetHostInterface()
38 	:
39 	BLooper(),
40 	fListeners(),
41 	fTeamDebuggers(20, false)
42 {
43 }
44 
45 
46 TargetHostInterface::~TargetHostInterface()
47 {
48 	for (ListenerList::Iterator it = fListeners.GetIterator();
49 			Listener* listener = it.Next();) {
50 		listener->TargetHostInterfaceQuit(this);
51 	}
52 }
53 
54 
55 status_t
56 TargetHostInterface::StartTeamDebugger(const TeamDebuggerOptions& options)
57 {
58 	// we only want to stop in main for teams we're responsible for
59 	// creating ourselves.
60 	bool stopInMain = options.requestType == TEAM_DEBUGGER_REQUEST_CREATE;
61 	team_id team = options.team;
62 	thread_id thread = options.thread;
63 
64 	AutoLocker<TargetHostInterface> interfaceLocker(this);
65 	if (options.requestType == TEAM_DEBUGGER_REQUEST_CREATE) {
66 		status_t error = CreateTeam(options.commandLineArgc,
67 			options.commandLineArgv, team);
68 		if (error != B_OK)
69 			return error;
70 		thread = team;
71 	}
72 
73 	if (options.requestType != TEAM_DEBUGGER_REQUEST_LOAD_CORE) {
74 
75 		if (team < 0 && thread < 0)
76 			return B_BAD_VALUE;
77 
78 		if (team < 0) {
79 			status_t error = FindTeamByThread(thread, team);
80 			if (error != B_OK)
81 				return error;
82 		}
83 
84 		TeamDebugger* debugger = FindTeamDebugger(team);
85 		if (debugger != NULL) {
86 			debugger->Activate();
87 			return B_OK;
88 		}
89 	}
90 
91 	return _StartTeamDebugger(team, options, stopInMain);
92 }
93 
94 
95 int32
96 TargetHostInterface::CountTeamDebuggers() const
97 {
98 	return fTeamDebuggers.CountItems();
99 }
100 
101 
102 TeamDebugger*
103 TargetHostInterface::TeamDebuggerAt(int32 index) const
104 {
105 	return fTeamDebuggers.ItemAt(index);
106 }
107 
108 
109 TeamDebugger*
110 TargetHostInterface::FindTeamDebugger(team_id team) const
111 {
112 	for (int32 i = 0; i < fTeamDebuggers.CountItems(); i++) {
113 		TeamDebugger* debugger = fTeamDebuggers.ItemAt(i);
114 		if (debugger->TeamID() == team && !debugger->IsPostMortem())
115 			return debugger;
116 	}
117 
118 	return NULL;
119 }
120 
121 
122 status_t
123 TargetHostInterface::AddTeamDebugger(TeamDebugger* debugger)
124 {
125 	if (!fTeamDebuggers.BinaryInsert(debugger, &_CompareDebuggers))
126 		return B_NO_MEMORY;
127 
128 	return B_OK;
129 }
130 
131 
132 void
133 TargetHostInterface::RemoveTeamDebugger(TeamDebugger* debugger)
134 {
135 	for (int32 i = 0; i < fTeamDebuggers.CountItems(); i++) {
136 		if (fTeamDebuggers.ItemAt(i) == debugger) {
137 			fTeamDebuggers.RemoveItemAt(i);
138 			break;
139 		}
140 	}
141 }
142 
143 
144 void
145 TargetHostInterface::AddListener(Listener* listener)
146 {
147 	AutoLocker<TargetHostInterface> interfaceLocker(this);
148 	fListeners.Add(listener);
149 }
150 
151 
152 void
153 TargetHostInterface::RemoveListener(Listener* listener)
154 {
155 	AutoLocker<TargetHostInterface> interfaceLocker(this);
156 	fListeners.Remove(listener);
157 }
158 
159 
160 void
161 TargetHostInterface::Quit()
162 {
163 	if (fTeamDebuggers.CountItems() == 0)
164 		BLooper::Quit();
165 }
166 
167 
168 void
169 TargetHostInterface::MessageReceived(BMessage* message)
170 {
171 	switch (message->what) {
172 	case MSG_TEAM_DEBUGGER_QUIT:
173 	{
174 		thread_id thread;
175 		if (message->FindInt32("thread", &thread) == B_OK)
176 			wait_for_thread(thread, NULL);
177 		break;
178 	}
179 	case MSG_TEAM_RESTART_REQUESTED:
180 	{
181 		int32 teamID;
182 		if (message->FindInt32("team", &teamID) != B_OK)
183 			break;
184 
185 		TeamDebugger* debugger = FindTeamDebugger(teamID);
186 
187 		UserInterface* userInterface = debugger->GetUserInterface()->Clone();
188 		if (userInterface == NULL)
189 			break;
190 
191 		BReference<UserInterface> userInterfaceReference(userInterface, true);
192 
193 		TeamDebuggerOptions options;
194 		options.requestType = TEAM_DEBUGGER_REQUEST_CREATE;
195 		options.commandLineArgc = debugger->ArgumentCount();
196 		options.commandLineArgv = debugger->Arguments();
197 		options.settingsManager = debugger->GetSettingsManager();
198 		options.userInterface = userInterface;
199 		status_t result = StartTeamDebugger(options);
200 		if (result == B_OK) {
201 			userInterfaceReference.Detach();
202 			debugger->PostMessage(B_QUIT_REQUESTED);
203 		}
204 		break;
205 	}
206 	default:
207 		BLooper::MessageReceived(message);
208 		break;
209 	}
210 }
211 
212 
213 void
214 TargetHostInterface::TeamDebuggerStarted(TeamDebugger* debugger)
215 {
216 	AutoLocker<TargetHostInterface> locker(this);
217 	AddTeamDebugger(debugger);
218 	_NotifyTeamDebuggerStarted(debugger);
219 }
220 
221 
222 void
223 TargetHostInterface::TeamDebuggerRestartRequested(TeamDebugger* debugger)
224 {
225 	BMessage message(MSG_TEAM_RESTART_REQUESTED);
226 	message.AddInt32("team", debugger->TeamID());
227 	PostMessage(&message);
228 }
229 
230 
231 void
232 TargetHostInterface::TeamDebuggerQuit(TeamDebugger* debugger)
233 {
234 	AutoLocker<TargetHostInterface> interfaceLocker(this);
235 	RemoveTeamDebugger(debugger);
236 
237 	if (debugger->Thread() >= 0) {
238 		_NotifyTeamDebuggerQuit(debugger);
239 		BMessage message(MSG_TEAM_DEBUGGER_QUIT);
240 		message.AddInt32("thread", debugger->Thread());
241 		PostMessage(&message);
242 	}
243 }
244 
245 
246 status_t
247 TargetHostInterface::_StartTeamDebugger(team_id teamID,
248 	const TeamDebuggerOptions& options, bool stopInMain)
249 {
250 	UserInterface* userInterface = options.userInterface;
251 	if (userInterface == NULL) {
252 		fprintf(stderr, "Error: Requested team debugger start without "
253 			"valid user interface!\n");
254 		return B_BAD_VALUE;
255 	}
256 
257 	thread_id threadID = options.thread;
258 	if (options.commandLineArgv != NULL)
259 		threadID = teamID;
260 
261 	DebuggerInterface* interface = NULL;
262 	TeamDebugger* debugger = NULL;
263 	status_t error = B_OK;
264 	if (options.requestType != TEAM_DEBUGGER_REQUEST_LOAD_CORE) {
265 		error = Attach(teamID, options.thread, interface);
266 		if (error != B_OK) {
267 			fprintf(stderr, "Error: Failed to attach to team %" B_PRId32
268 				": %s!\n", teamID, strerror(error));
269 			return error;
270 		}
271 	} else {
272 		error = LoadCore(options.coreFilePath, interface, threadID);
273 		if (error != B_OK) {
274 			fprintf(stderr, "Error: Failed to load core file '%s': %s!\n",
275 				options.coreFilePath, strerror(error));
276 			return error;
277 		}
278 	}
279 
280 	BReference<DebuggerInterface> debuggerInterfaceReference(interface,
281 		true);
282 	debugger = new(std::nothrow) TeamDebugger(this, userInterface,
283 		options.settingsManager);
284 	if (debugger != NULL) {
285 		error = debugger->Init(interface, threadID,
286 			options.commandLineArgc, options.commandLineArgv, stopInMain);
287 	}
288 
289 	if (error != B_OK) {
290 		printf("Error: debugger for team %" B_PRId32 " on interface %s failed"
291 			" to init: %s!\n", interface->TeamID(), Name(), strerror(error));
292 		delete debugger;
293 		debugger = NULL;
294 	} else {
295 		printf("debugger for team %" B_PRId32 " on interface %s created and"
296 			" initialized successfully!\n", interface->TeamID(), Name());
297 	}
298 
299 	return error;
300 }
301 
302 
303 void
304 TargetHostInterface::_NotifyTeamDebuggerStarted(TeamDebugger* debugger)
305 {
306 	for (ListenerList::Iterator it = fListeners.GetIterator();
307 			Listener* listener = it.Next();) {
308 		listener->TeamDebuggerStarted(debugger);
309 	}
310 }
311 
312 
313 void
314 TargetHostInterface::_NotifyTeamDebuggerQuit(TeamDebugger* debugger)
315 {
316 	for (ListenerList::Iterator it = fListeners.GetIterator();
317 			Listener* listener = it.Next();) {
318 		listener->TeamDebuggerQuit(debugger);
319 	}
320 }
321 
322 
323 /*static*/ int
324 TargetHostInterface::_CompareDebuggers(const TeamDebugger* a,
325 	const TeamDebugger* b)
326 {
327 	return a->TeamID() < b->TeamID() ? -1 : 1;
328 }
329 
330 
331 // #pragma mark - TargetHostInterface::Listener
332 
333 
334 TargetHostInterface::Listener::~Listener()
335 {
336 }
337 
338 
339 void
340 TargetHostInterface::Listener::TeamDebuggerStarted(TeamDebugger* debugger)
341 {
342 }
343 
344 
345 void
346 TargetHostInterface::Listener::TeamDebuggerQuit(TeamDebugger* debugger)
347 {
348 }
349 
350 
351 void
352 TargetHostInterface::Listener::TargetHostInterfaceQuit(
353 	TargetHostInterface* interface)
354 {
355 }
356