xref: /haiku/src/kits/debugger/target_host_interface/local/LocalTargetHostInterface.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
1 /*
2  * Copyright 2016, Rene Gollent, rene@gollent.com.
3  * Copyright 2016, Ingo Weinhold, ingo_weinhold@gmx.de.
4  * Distributed under the terms of the MIT License.
5  */
6 
7 #include "LocalTargetHostInterface.h"
8 
9 #include <set>
10 
11 #include <stdio.h>
12 #include <unistd.h>
13 
14 #include <image.h>
15 
16 #include <AutoDeleter.h>
17 #include <AutoLocker.h>
18 #include <system_info.h>
19 #include <util/KMessage.h>
20 
21 #include "debug_utils.h"
22 
23 #include "CoreFile.h"
24 #include "CoreFileDebuggerInterface.h"
25 #include "LocalDebuggerInterface.h"
26 #include "TargetHost.h"
27 
28 using std::set;
29 
30 LocalTargetHostInterface::LocalTargetHostInterface()
31 	:
32 	TargetHostInterface(),
33 	fTargetHost(NULL),
34 	fDataPort(-1)
35 {
36 	SetName("Local");
37 }
38 
39 
40 LocalTargetHostInterface::~LocalTargetHostInterface()
41 {
42 	Close();
43 
44 	if (fTargetHost != NULL)
45 		fTargetHost->ReleaseReference();
46 }
47 
48 
49 status_t
50 LocalTargetHostInterface::Init(Settings* settings)
51 {
52 	char hostname[HOST_NAME_MAX + 1];
53 	status_t error = gethostname(hostname, sizeof(hostname));
54 	if (error != B_OK) {
55 		fprintf(stderr, "gethostname() failed, defaults to localhost\n");
56 		strlcpy(hostname, "localhost", sizeof(hostname));
57 	}
58 
59 	fTargetHost = new(std::nothrow) TargetHost(hostname);
60 	if (fTargetHost == NULL)
61 		return B_NO_MEMORY;
62 
63 	team_info info;
64 	error = get_team_info(B_CURRENT_TEAM, &info);
65 	if (error != B_OK)
66 		return error;
67 
68 	char buffer[128];
69 	snprintf(buffer, sizeof(buffer), "LocalTargetHostInterface %" B_PRId32,
70 		info.team);
71 
72 	fDataPort = create_port(100, buffer);
73 	if (fDataPort < 0)
74 		return fDataPort;
75 
76 	fPortWorker = spawn_thread(_PortLoop, "Local Target Host Loop",
77 		B_NORMAL_PRIORITY, this);
78 	if (fPortWorker < 0)
79 		return fPortWorker;
80 
81 	resume_thread(fPortWorker);
82 
83 	AutoLocker<TargetHost> hostLocker(fTargetHost);
84 
85 	error = __start_watching_system(-1,
86 		B_WATCH_SYSTEM_TEAM_CREATION | B_WATCH_SYSTEM_TEAM_DELETION,
87 		fDataPort, 0);
88 	if (error != B_OK)
89 		return error;
90 
91 	int32 cookie = 0;
92 	while (get_next_team_info(&cookie, &info) == B_OK) {
93 		error = fTargetHost->AddTeam(info);
94 		if (error != B_OK)
95 			return error;
96 	}
97 
98 	snprintf(buffer, sizeof(buffer), "Local (%s)", hostname);
99 	SetName(buffer);
100 
101 	return B_OK;
102 }
103 
104 
105 void
106 LocalTargetHostInterface::Close()
107 {
108 	if (fDataPort > 0) {
109 		__stop_watching_system(-1,
110 			B_WATCH_SYSTEM_TEAM_CREATION | B_WATCH_SYSTEM_TEAM_DELETION,
111 			fDataPort, 0);
112 
113 		delete_port(fDataPort);
114 		fDataPort = -1;
115 	}
116 
117 	if (fPortWorker > 0) {
118 		wait_for_thread(fPortWorker, NULL);
119 		fPortWorker = -1;
120 	}
121 }
122 
123 
124 bool
125 LocalTargetHostInterface::IsLocal() const
126 {
127 	return true;
128 }
129 
130 
131 bool
132 LocalTargetHostInterface::Connected() const
133 {
134 	return true;
135 }
136 
137 
138 TargetHost*
139 LocalTargetHostInterface::GetTargetHost()
140 {
141 	return fTargetHost;
142 }
143 
144 
145 status_t
146 LocalTargetHostInterface::Attach(team_id teamID, thread_id threadID,
147 	DebuggerInterface*& _interface) const
148 {
149 	if (teamID < 0 && threadID < 0)
150 		return B_BAD_VALUE;
151 
152 	status_t error;
153 	if (teamID < 0) {
154 		thread_info threadInfo;
155 		error = get_thread_info(threadID, &threadInfo);
156 		if (error != B_OK)
157 			return error;
158 
159 		teamID = threadInfo.team;
160 	}
161 
162 	LocalDebuggerInterface* interface
163 		= new(std::nothrow) LocalDebuggerInterface(teamID);
164 	if (interface == NULL)
165 		return B_NO_MEMORY;
166 
167 	BReference<DebuggerInterface> interfaceReference(interface, true);
168 	error = interface->Init();
169 	if (error != B_OK)
170 		return error;
171 
172 	_interface = interface;
173 	interfaceReference.Detach();
174 	return B_OK;
175 }
176 
177 
178 status_t
179 LocalTargetHostInterface::CreateTeam(int commandLineArgc,
180 	const char* const* arguments, team_id& _teamID) const
181 {
182 	thread_id thread = load_program(arguments, commandLineArgc, false);
183 	if (thread < 0)
184 		return thread;
185 
186 	// main thread ID == team ID.
187 	_teamID = thread;
188 	return B_OK;
189 }
190 
191 
192 status_t
193 LocalTargetHostInterface::LoadCore(const char* coreFilePath,
194 	DebuggerInterface*& _interface, thread_id& _thread) const
195 {
196 	// load the core file
197 	CoreFile* coreFile = new(std::nothrow) CoreFile;
198 	if (coreFile == NULL)
199 		return B_NO_MEMORY;
200 	ObjectDeleter<CoreFile> coreFileDeleter(coreFile);
201 
202 	status_t error = coreFile->Init(coreFilePath);
203 	if (error != B_OK)
204 		return error;
205 
206 	// create the debugger interface
207 	CoreFileDebuggerInterface* interface
208 		= new(std::nothrow) CoreFileDebuggerInterface(coreFile);
209 	if (interface == NULL)
210 		return B_NO_MEMORY;
211 	coreFileDeleter.Detach();
212 
213 	BReference<DebuggerInterface> interfaceReference(interface, true);
214 	error = interface->Init();
215 	if (error != B_OK)
216 		return error;
217 
218 	const CoreFileTeamInfo& teamInfo = coreFile->GetTeamInfo();
219 	_thread = teamInfo.Id();
220 	_interface = interface;
221 	interfaceReference.Detach();
222 
223 	return B_OK;
224 }
225 
226 
227 status_t
228 LocalTargetHostInterface::FindTeamByThread(thread_id thread,
229 	team_id& _teamID) const
230 {
231 	thread_info info;
232 	status_t error = get_thread_info(thread, &info);
233 	if (error != B_OK)
234 		return error;
235 
236 	_teamID = info.team;
237 	return B_OK;
238 }
239 
240 
241 status_t
242 LocalTargetHostInterface::_PortLoop(void* arg)
243 {
244 	LocalTargetHostInterface* interface = (LocalTargetHostInterface*)arg;
245 	set<team_id> waitingTeams;
246 
247 	for (;;) {
248 		status_t error;
249 		bool addToWaiters;
250 		char buffer[2048];
251 		int32 messageCode;
252 		team_id team;
253 
254 		ssize_t size = read_port_etc(interface->fDataPort, &messageCode,
255 			buffer, sizeof(buffer), B_TIMEOUT, waitingTeams.empty()
256 				? B_INFINITE_TIMEOUT : 20000);
257 		if (size == B_INTERRUPTED)
258 			continue;
259 		else if (size == B_TIMED_OUT && !waitingTeams.empty()) {
260 			for (set<team_id>::iterator it = waitingTeams.begin();
261 				it != waitingTeams.end(); ++it) {
262 				team = *it;
263 				error = interface->_HandleTeamEvent(team,
264 					B_TEAM_CREATED, addToWaiters);
265 				if (error != B_OK)
266 					continue;
267 				else if (!addToWaiters) {
268 					waitingTeams.erase(it);
269 					if (waitingTeams.empty())
270 						break;
271 					it = waitingTeams.begin();
272 				}
273 			}
274 			continue;
275 		} else if (size < 0)
276 			return size;
277 
278 		KMessage message;
279 		size = message.SetTo(buffer);
280 		if (size != B_OK)
281 			continue;
282 
283 		if (message.What() != B_SYSTEM_OBJECT_UPDATE)
284 			continue;
285 
286 		int32 opcode = 0;
287 		if (message.FindInt32("opcode", &opcode) != B_OK)
288 			continue;
289 
290 		team = -1;
291 		if (message.FindInt32("team", &team) != B_OK)
292 			continue;
293 
294 		error = interface->_HandleTeamEvent(team, opcode,
295 			addToWaiters);
296 		if (error != B_OK)
297 			continue;
298 		if (opcode == B_TEAM_CREATED && addToWaiters) {
299 			try {
300 				waitingTeams.insert(team);
301 			} catch (...) {
302 				continue;
303 			}
304 		}
305 	}
306 
307 	return B_OK;
308 }
309 
310 
311 status_t
312 LocalTargetHostInterface::_HandleTeamEvent(team_id team, int32 opcode,
313 	bool& addToWaiters)
314 {
315 	addToWaiters = false;
316 	AutoLocker<TargetHost> locker(fTargetHost);
317 	switch (opcode) {
318 		case B_TEAM_CREATED:
319 		case B_TEAM_EXEC:
320 		{
321 			team_info info;
322 			status_t error = get_team_info(team, &info);
323 			// this team is already gone, no point in sending a notification
324 			if (error == B_BAD_TEAM_ID)
325 				return B_OK;
326 			else if (error != B_OK)
327 				return error;
328 			else {
329 				int32 cookie = 0;
330 				image_info imageInfo;
331 				addToWaiters = true;
332 				while (get_next_image_info(team, &cookie, &imageInfo)
333 					== B_OK) {
334 					if (imageInfo.type == B_APP_IMAGE) {
335 						addToWaiters = false;
336 						break;
337 					}
338 				}
339 				if (addToWaiters)
340 					return B_OK;
341 			}
342 
343 			if (opcode == B_TEAM_CREATED)
344 				fTargetHost->AddTeam(info);
345 			else
346 				fTargetHost->UpdateTeam(info);
347 			break;
348 		}
349 
350 		case B_TEAM_DELETED:
351 		{
352 			fTargetHost->RemoveTeam(team);
353 			break;
354 		}
355 
356 		default:
357 		{
358 			break;
359 		}
360 	}
361 
362 	return B_OK;
363 }
364