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
LocalTargetHostInterface()30 LocalTargetHostInterface::LocalTargetHostInterface()
31 :
32 TargetHostInterface(),
33 fTargetHost(NULL),
34 fDataPort(-1)
35 {
36 SetName("Local");
37 }
38
39
~LocalTargetHostInterface()40 LocalTargetHostInterface::~LocalTargetHostInterface()
41 {
42 Close();
43
44 if (fTargetHost != NULL)
45 fTargetHost->ReleaseReference();
46 }
47
48
49 status_t
Init(Settings * settings)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
Close()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
IsLocal() const125 LocalTargetHostInterface::IsLocal() const
126 {
127 return true;
128 }
129
130
131 bool
Connected() const132 LocalTargetHostInterface::Connected() const
133 {
134 return true;
135 }
136
137
138 TargetHost*
GetTargetHost()139 LocalTargetHostInterface::GetTargetHost()
140 {
141 return fTargetHost;
142 }
143
144
145 status_t
Attach(team_id teamID,thread_id threadID,DebuggerInterface * & _interface) const146 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
CreateTeam(int commandLineArgc,const char * const * arguments,team_id & _teamID) const179 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
LoadCore(const char * coreFilePath,DebuggerInterface * & _interface,thread_id & _thread) const193 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
FindTeamByThread(thread_id thread,team_id & _teamID) const228 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
_PortLoop(void * arg)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
_HandleTeamEvent(team_id team,int32 opcode,bool & addToWaiters)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