MachThreadList.cpp revision 2c45f5425f35af30efb4f2751890decf292e8eaf
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 22MachThreadList::MachThreadList() : 23 m_threads(), 24 m_threads_mutex(PTHREAD_MUTEX_RECURSIVE) 25{ 26} 27 28MachThreadList::~MachThreadList() 29{ 30} 31 32nub_state_t 33MachThreadList::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 41const char * 42MachThreadList::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 50nub_thread_t 51MachThreadList::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 63bool 64MachThreadList::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 72bool 73MachThreadList::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 79void 80MachThreadList::DumpThreadStoppedReason (nub_thread_t tid) const 81{ 82 MachThreadSP thread_sp (GetThreadByID (tid)); 83 if (thread_sp) 84 thread_sp->GetStopException().DumpStopReason(); 85} 86 87const char * 88MachThreadList::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 96MachThreadSP 97MachThreadList::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 125bool 126MachThreadList::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 135bool 136MachThreadList::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 145nub_size_t 146MachThreadList::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 154nub_size_t 155MachThreadList::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 163nub_size_t 164MachThreadList::NumThreads () const 165{ 166 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 167 return m_threads.size(); 168} 169 170nub_thread_t 171MachThreadList::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 179nub_thread_t 180MachThreadList::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 189bool 190MachThreadList::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 201void 202MachThreadList::Clear() 203{ 204 PTHREAD_MUTEX_LOCKER (locker, m_threads_mutex); 205 m_threads.clear(); 206} 207 208uint32_t 209MachThreadList::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 293void 294MachThreadList::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 316void 317MachThreadList::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 328void 329MachThreadList::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 406uint32_t 407MachThreadList::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//---------------------------------------------------------------------- 430bool 431MachThreadList::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 444void 445MachThreadList::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 456uint32_t 457MachThreadList::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 468bool 469MachThreadList::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 480uint32_t 481MachThreadList::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 492bool 493MachThreadList::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 504uint32_t 505MachThreadList::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