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
20TeamDebuggerOptions::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
37TargetHostInterface::TargetHostInterface()
38	:
39	BLooper(),
40	fListeners(),
41	fTeamDebuggers(20, false)
42{
43}
44
45
46TargetHostInterface::~TargetHostInterface()
47{
48	for (ListenerList::Iterator it = fListeners.GetIterator();
49			Listener* listener = it.Next();) {
50		listener->TargetHostInterfaceQuit(this);
51	}
52}
53
54
55status_t
56TargetHostInterface::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
95int32
96TargetHostInterface::CountTeamDebuggers() const
97{
98	return fTeamDebuggers.CountItems();
99}
100
101
102TeamDebugger*
103TargetHostInterface::TeamDebuggerAt(int32 index) const
104{
105	return fTeamDebuggers.ItemAt(index);
106}
107
108
109TeamDebugger*
110TargetHostInterface::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
122status_t
123TargetHostInterface::AddTeamDebugger(TeamDebugger* debugger)
124{
125	if (!fTeamDebuggers.BinaryInsert(debugger, &_CompareDebuggers))
126		return B_NO_MEMORY;
127
128	return B_OK;
129}
130
131
132void
133TargetHostInterface::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
144void
145TargetHostInterface::AddListener(Listener* listener)
146{
147	AutoLocker<TargetHostInterface> interfaceLocker(this);
148	fListeners.Add(listener);
149}
150
151
152void
153TargetHostInterface::RemoveListener(Listener* listener)
154{
155	AutoLocker<TargetHostInterface> interfaceLocker(this);
156	fListeners.Remove(listener);
157}
158
159
160void
161TargetHostInterface::Quit()
162{
163	if (fTeamDebuggers.CountItems() == 0)
164		BLooper::Quit();
165}
166
167
168void
169TargetHostInterface::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
213void
214TargetHostInterface::TeamDebuggerStarted(TeamDebugger* debugger)
215{
216	AutoLocker<TargetHostInterface> locker(this);
217	AddTeamDebugger(debugger);
218	_NotifyTeamDebuggerStarted(debugger);
219}
220
221
222void
223TargetHostInterface::TeamDebuggerRestartRequested(TeamDebugger* debugger)
224{
225	BMessage message(MSG_TEAM_RESTART_REQUESTED);
226	message.AddInt32("team", debugger->TeamID());
227	PostMessage(&message);
228}
229
230
231void
232TargetHostInterface::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
246status_t
247TargetHostInterface::_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
303void
304TargetHostInterface::_NotifyTeamDebuggerStarted(TeamDebugger* debugger)
305{
306	for (ListenerList::Iterator it = fListeners.GetIterator();
307			Listener* listener = it.Next();) {
308		listener->TeamDebuggerStarted(debugger);
309	}
310}
311
312
313void
314TargetHostInterface::_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
324TargetHostInterface::_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
334TargetHostInterface::Listener::~Listener()
335{
336}
337
338
339void
340TargetHostInterface::Listener::TeamDebuggerStarted(TeamDebugger* debugger)
341{
342}
343
344
345void
346TargetHostInterface::Listener::TeamDebuggerQuit(TeamDebugger* debugger)
347{
348}
349
350
351void
352TargetHostInterface::Listener::TargetHostInterfaceQuit(
353	TargetHostInterface* interface)
354{
355}
356