1 //===-- MachThreadList.cpp --------------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Created by Greg Clayton on 6/19/07. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #include "MachThreadList.h" 15 16 #include <sys/sysctl.h> 17 18 #include "DNBLog.h" 19 #include "DNBThreadResumeActions.h" 20 #include "MachProcess.h" 21 22 MachThreadList::MachThreadList() : 23 m_threads(), 24 m_threads_mutex(PTHREAD_MUTEX_RECURSIVE) 25 { 26 } 27 28 MachThreadList::~MachThreadList() 29 { 30 } 31 32 nub_state_t 33 MachThreadList::GetState(thread_t tid) 34 { 35 MachThreadSP thread_sp (GetThreadByID (tid)); 36 if (thread_sp) 37 return thread_sp->GetState(); 38 return eStateInvalid; 39 } 40 41 const char * 42 MachThreadList::GetName (thread_t tid) 43 { 44 MachThreadSP thread_sp (GetThreadByID (tid)); 45 if (thread_sp) 46 return thread_sp->GetName(); 47 return NULL; 48 } 49 50 nub_thread_t 51 MachThreadList::SetCurrentThread(thread_t tid) 52 { 53 MachThreadSP thread_sp (GetThreadByID (tid)); 54 if (thread_sp) 55 { 56 m_current_thread = thread_sp; 57 return tid; 58 } 59 return INVALID_NUB_THREAD; 60 } 61 62 63 bool 64 MachThreadList::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const 65 { 66 MachThreadSP thread_sp (GetThreadByID (tid)); 67 if (thread_sp) 68 return thread_sp->GetStopException().GetStopInfo(stop_info); 69 return false; 70 } 71 72 bool 73 MachThreadList::GetIdentifierInfo (nub_thread_t tid, thread_identifier_info_data_t *ident_info) 74 { 75 mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; 76 return ::thread_info (tid, THREAD_IDENTIFIER_INFO, (thread_info_t)ident_info, &count) == KERN_SUCCESS; 77 } 78 79 void 80 MachThreadList::DumpThreadStoppedReason (nub_thread_t tid) const 81 { 82 MachThreadSP thread_sp (GetThreadByID (tid)); 83 if (thread_sp) 84 thread_sp->GetStopException().DumpStopReason(); 85 } 86 87 const char * 88 MachThreadList::GetThreadInfo (nub_thread_t tid) const 89 { 90 MachThreadSP thread_sp (GetThreadByID (tid)); 91 if (thread_sp) 92 return thread_sp->GetBasicInfoAsString(); 93 return NULL; 94 } 95 96 MachThreadSP 97 MachThreadList::GetThreadByID (nub_thread_t tid) const 98 { 99 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 100 MachThreadSP thread_sp; 101 const size_t num_threads = m_threads.size(); 102 if (MachThread::ThreadIDIsValid(tid)) 103 { 104 for (size_t idx = 0; idx < num_threads; ++idx) 105 { 106 if (m_threads[idx]->ThreadID() == tid) 107 { 108 thread_sp = m_threads[idx]; 109 break; 110 } 111 } 112 } 113 else if (num_threads > 0) 114 { 115 // See DNBWatchpointSet() -> MachProcess::CreateWatchpoint() -> MachProcess::EnableWatchpoint() 116 // -> MachThreadList::EnableHardwareWatchpoint() for a use case of this branch. 117 if (m_current_thread) 118 thread_sp = m_current_thread; 119 else 120 thread_sp = m_threads[0]; 121 } 122 return thread_sp; 123 } 124 125 bool 126 MachThreadList::GetRegisterValue ( nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, DNBRegisterValue *reg_value ) const 127 { 128 MachThreadSP thread_sp (GetThreadByID (tid)); 129 if (thread_sp) 130 return thread_sp->GetRegisterValue(reg_set_idx, reg_idx, reg_value); 131 132 return false; 133 } 134 135 bool 136 MachThreadList::SetRegisterValue ( nub_thread_t tid, uint32_t reg_set_idx, uint32_t reg_idx, const DNBRegisterValue *reg_value ) const 137 { 138 MachThreadSP thread_sp (GetThreadByID (tid)); 139 if (thread_sp) 140 return thread_sp->SetRegisterValue(reg_set_idx, reg_idx, reg_value); 141 142 return false; 143 } 144 145 nub_size_t 146 MachThreadList::GetRegisterContext (nub_thread_t tid, void *buf, size_t buf_len) 147 { 148 MachThreadSP thread_sp (GetThreadByID (tid)); 149 if (thread_sp) 150 return thread_sp->GetRegisterContext (buf, buf_len); 151 return 0; 152 } 153 154 nub_size_t 155 MachThreadList::SetRegisterContext (nub_thread_t tid, const void *buf, size_t buf_len) 156 { 157 MachThreadSP thread_sp (GetThreadByID (tid)); 158 if (thread_sp) 159 return thread_sp->SetRegisterContext (buf, buf_len); 160 return 0; 161 } 162 163 nub_size_t 164 MachThreadList::NumThreads () const 165 { 166 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 167 return m_threads.size(); 168 } 169 170 nub_thread_t 171 MachThreadList::ThreadIDAtIndex (nub_size_t idx) const 172 { 173 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 174 if (idx < m_threads.size()) 175 return m_threads[idx]->ThreadID(); 176 return INVALID_NUB_THREAD; 177 } 178 179 nub_thread_t 180 MachThreadList::CurrentThreadID ( ) 181 { 182 MachThreadSP thread_sp; 183 CurrentThread(thread_sp); 184 if (thread_sp.get()) 185 return thread_sp->ThreadID(); 186 return INVALID_NUB_THREAD; 187 } 188 189 bool 190 MachThreadList::NotifyException(MachException::Data& exc) 191 { 192 MachThreadSP thread_sp (GetThreadByID (exc.thread_port)); 193 if (thread_sp) 194 { 195 thread_sp->NotifyException(exc); 196 return true; 197 } 198 return false; 199 } 200 201 void 202 MachThreadList::Clear() 203 { 204 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 205 m_threads.clear(); 206 } 207 208 uint32_t 209 MachThreadList::UpdateThreadList(MachProcess *process, bool update, MachThreadList::collection *new_threads) 210 { 211 // locker will keep a mutex locked until it goes out of scope 212 DNBLogThreadedIf (LOG_THREAD, "MachThreadList::UpdateThreadList (pid = %4.4x, update = %u) process stop count = %u", process->ProcessID(), update, process->StopCount()); 213 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 214 215 #if defined (__i386__) || defined (__x86_64__) 216 if (process->StopCount() == 0) 217 { 218 int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process->ProcessID() }; 219 struct kinfo_proc processInfo; 220 size_t bufsize = sizeof(processInfo); 221 bool is_64_bit = false; 222 if (sysctl(mib, (unsigned)(sizeof(mib)/sizeof(int)), &processInfo, &bufsize, NULL, 0) == 0 && bufsize > 0) 223 { 224 if (processInfo.kp_proc.p_flag & P_LP64) 225 is_64_bit = true; 226 } 227 if (is_64_bit) 228 DNBArchProtocol::SetArchitecture(CPU_TYPE_X86_64); 229 else 230 DNBArchProtocol::SetArchitecture(CPU_TYPE_I386); 231 } 232 #endif 233 234 if (m_threads.empty() || update) 235 { 236 thread_array_t thread_list = NULL; 237 mach_msg_type_number_t thread_list_count = 0; 238 task_t task = process->Task().TaskPort(); 239 DNBError err(::task_threads (task, &thread_list, &thread_list_count), DNBError::MachKernel); 240 241 if (DNBLogCheckLogBit(LOG_THREAD) || err.Fail()) 242 err.LogThreaded("::task_threads ( task = 0x%4.4x, thread_list => %p, thread_list_count => %u )", task, thread_list, thread_list_count); 243 244 if (err.Error() == KERN_SUCCESS && thread_list_count > 0) 245 { 246 MachThreadList::collection currThreads; 247 size_t idx; 248 // Iterator through the current thread list and see which threads 249 // we already have in our list (keep them), which ones we don't 250 // (add them), and which ones are not around anymore (remove them). 251 for (idx = 0; idx < thread_list_count; ++idx) 252 { 253 const thread_t tid = thread_list[idx]; 254 255 MachThreadSP thread_sp (GetThreadByID (tid)); 256 if (thread_sp) 257 { 258 // Keep the existing thread class 259 currThreads.push_back(thread_sp); 260 } 261 else 262 { 263 // We don't have this thread, lets add it. 264 thread_sp.reset(new MachThread(process, tid)); 265 266 // Add the new thread regardless of its is user ready state... 267 // Make sure the thread is ready to be displayed and shown to users 268 // before we add this thread to our list... 269 if (thread_sp->IsUserReady()) 270 { 271 if (new_threads) 272 new_threads->push_back(thread_sp); 273 274 currThreads.push_back(thread_sp); 275 } 276 } 277 } 278 279 m_threads.swap(currThreads); 280 m_current_thread.reset(); 281 282 // Free the vm memory given to us by ::task_threads() 283 vm_size_t thread_list_size = (vm_size_t) (thread_list_count * sizeof (thread_t)); 284 ::vm_deallocate (::mach_task_self(), 285 (vm_address_t)thread_list, 286 thread_list_size); 287 } 288 } 289 return m_threads.size(); 290 } 291 292 293 void 294 MachThreadList::CurrentThread (MachThreadSP& thread_sp) 295 { 296 // locker will keep a mutex locked until it goes out of scope 297 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 298 if (m_current_thread.get() == NULL) 299 { 300 // Figure out which thread is going to be our current thread. 301 // This is currently done by finding the first thread in the list 302 // that has a valid exception. 303 const uint32_t num_threads = m_threads.size(); 304 for (uint32_t idx = 0; idx < num_threads; ++idx) 305 { 306 if (m_threads[idx]->GetStopException().IsValid()) 307 { 308 m_current_thread = m_threads[idx]; 309 break; 310 } 311 } 312 } 313 thread_sp = m_current_thread; 314 } 315 316 void 317 MachThreadList::Dump() const 318 { 319 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 320 const uint32_t num_threads = m_threads.size(); 321 for (uint32_t idx = 0; idx < num_threads; ++idx) 322 { 323 m_threads[idx]->Dump(idx); 324 } 325 } 326 327 328 void 329 MachThreadList::ProcessWillResume(MachProcess *process, const DNBThreadResumeActions &thread_actions) 330 { 331 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 332 333 // Update our thread list, because sometimes libdispatch or the kernel 334 // will spawn threads while a task is suspended. 335 MachThreadList::collection new_threads; 336 337 // First figure out if we were planning on running only one thread, and if so force that thread to resume. 338 bool run_one_thread; 339 nub_thread_t solo_thread = INVALID_NUB_THREAD; 340 if (thread_actions.GetSize() > 0 341 && thread_actions.NumActionsWithState(eStateStepping) + thread_actions.NumActionsWithState (eStateRunning) == 1) 342 { 343 run_one_thread = true; 344 const DNBThreadResumeAction *action_ptr = thread_actions.GetFirst(); 345 size_t num_actions = thread_actions.GetSize(); 346 for (size_t i = 0; i < num_actions; i++, action_ptr++) 347 { 348 if (action_ptr->state == eStateStepping || action_ptr->state == eStateRunning) 349 { 350 solo_thread = action_ptr->tid; 351 break; 352 } 353 } 354 } 355 else 356 run_one_thread = false; 357 358 UpdateThreadList(process, true, &new_threads); 359 360 DNBThreadResumeAction resume_new_threads = { -1, eStateRunning, 0, INVALID_NUB_ADDRESS }; 361 // If we are planning to run only one thread, any new threads should be suspended. 362 if (run_one_thread) 363 resume_new_threads.state = eStateSuspended; 364 365 const uint32_t num_new_threads = new_threads.size(); 366 const uint32_t num_threads = m_threads.size(); 367 for (uint32_t idx = 0; idx < num_threads; ++idx) 368 { 369 MachThread *thread = m_threads[idx].get(); 370 bool handled = false; 371 for (uint32_t new_idx = 0; new_idx < num_new_threads; ++new_idx) 372 { 373 if (thread == new_threads[new_idx].get()) 374 { 375 thread->ThreadWillResume(&resume_new_threads); 376 handled = true; 377 break; 378 } 379 } 380 381 if (!handled) 382 { 383 const DNBThreadResumeAction *thread_action = thread_actions.GetActionForThread (thread->ThreadID(), true); 384 // There must always be a thread action for every thread. 385 assert (thread_action); 386 bool others_stopped = false; 387 if (solo_thread == thread->ThreadID()) 388 others_stopped = true; 389 thread->ThreadWillResume (thread_action, others_stopped); 390 } 391 } 392 393 if (new_threads.size()) 394 { 395 for (uint32_t idx = 0; idx < num_new_threads; ++idx) 396 { 397 DNBLogThreadedIf (LOG_THREAD, "MachThreadList::ProcessWillResume (pid = %4.4x) stop-id=%u, resuming newly discovered thread: 0x%4.4x, thread-is-user-ready=%i)", 398 process->ProcessID(), 399 process->StopCount(), 400 new_threads[idx]->ThreadID(), 401 new_threads[idx]->IsUserReady()); 402 } 403 } 404 } 405 406 uint32_t 407 MachThreadList::ProcessDidStop(MachProcess *process) 408 { 409 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 410 // Update our thread list 411 const uint32_t num_threads = UpdateThreadList(process, true); 412 for (uint32_t idx = 0; idx < num_threads; ++idx) 413 { 414 m_threads[idx]->ThreadDidStop(); 415 } 416 return num_threads; 417 } 418 419 //---------------------------------------------------------------------- 420 // Check each thread in our thread list to see if we should notify our 421 // client of the current halt in execution. 422 // 423 // Breakpoints can have callback functions associated with them than 424 // can return true to stop, or false to continue executing the inferior. 425 // 426 // RETURNS 427 // true if we should stop and notify our clients 428 // false if we should resume our child process and skip notification 429 //---------------------------------------------------------------------- 430 bool 431 MachThreadList::ShouldStop(bool &step_more) 432 { 433 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 434 uint32_t should_stop = false; 435 const uint32_t num_threads = m_threads.size(); 436 for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) 437 { 438 should_stop = m_threads[idx]->ShouldStop(step_more); 439 } 440 return should_stop; 441 } 442 443 444 void 445 MachThreadList::NotifyBreakpointChanged (const DNBBreakpoint *bp) 446 { 447 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 448 const uint32_t num_threads = m_threads.size(); 449 for (uint32_t idx = 0; idx < num_threads; ++idx) 450 { 451 m_threads[idx]->NotifyBreakpointChanged(bp); 452 } 453 } 454 455 456 uint32_t 457 MachThreadList::EnableHardwareBreakpoint (const DNBBreakpoint* bp) const 458 { 459 if (bp != NULL) 460 { 461 MachThreadSP thread_sp (GetThreadByID (bp->ThreadID())); 462 if (thread_sp) 463 return thread_sp->EnableHardwareBreakpoint(bp); 464 } 465 return INVALID_NUB_HW_INDEX; 466 } 467 468 bool 469 MachThreadList::DisableHardwareBreakpoint (const DNBBreakpoint* bp) const 470 { 471 if (bp != NULL) 472 { 473 MachThreadSP thread_sp (GetThreadByID (bp->ThreadID())); 474 if (thread_sp) 475 return thread_sp->DisableHardwareBreakpoint(bp); 476 } 477 return false; 478 } 479 480 uint32_t 481 MachThreadList::EnableHardwareWatchpoint (const DNBBreakpoint* wp) const 482 { 483 if (wp != NULL) 484 { 485 MachThreadSP thread_sp (GetThreadByID (wp->ThreadID())); 486 if (thread_sp) 487 return thread_sp->EnableHardwareWatchpoint(wp); 488 } 489 return INVALID_NUB_HW_INDEX; 490 } 491 492 bool 493 MachThreadList::DisableHardwareWatchpoint (const DNBBreakpoint* wp) const 494 { 495 if (wp != NULL) 496 { 497 MachThreadSP thread_sp (GetThreadByID (wp->ThreadID())); 498 if (thread_sp) 499 return thread_sp->DisableHardwareWatchpoint(wp); 500 } 501 return false; 502 } 503 504 uint32_t 505 MachThreadList::GetThreadIndexForThreadStoppedWithSignal (const int signo) const 506 { 507 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 508 uint32_t should_stop = false; 509 const uint32_t num_threads = m_threads.size(); 510 for (uint32_t idx = 0; !should_stop && idx < num_threads; ++idx) 511 { 512 if (m_threads[idx]->GetStopException().SoftSignal () == signo) 513 return idx; 514 } 515 return UINT32_MAX; 516 } 517 518