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