1 //===-- CommandObjectThreadUtil.cpp -----------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "CommandObjectThreadUtil.h" 10 11 #include "lldb/Interpreter/CommandReturnObject.h" 12 #include "lldb/Target/Process.h" 13 #include "lldb/Target/Thread.h" 14 15 using namespace lldb; 16 using namespace lldb_private; 17 using namespace llvm; 18 19 CommandObjectIterateOverThreads::CommandObjectIterateOverThreads( 20 CommandInterpreter &interpreter, const char *name, const char *help, 21 const char *syntax, uint32_t flags) 22 : CommandObjectParsed(interpreter, name, help, syntax, flags) { 23 // These commands all take thread ID's as arguments. 24 AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatStar); 25 } 26 27 CommandObjectMultipleThreads::CommandObjectMultipleThreads( 28 CommandInterpreter &interpreter, const char *name, const char *help, 29 const char *syntax, uint32_t flags) 30 : CommandObjectParsed(interpreter, name, help, syntax, flags) { 31 // These commands all take thread ID's as arguments. 32 AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatStar); 33 } 34 35 void CommandObjectIterateOverThreads::DoExecute(Args &command, 36 CommandReturnObject &result) { 37 result.SetStatus(m_success_return); 38 39 bool all_threads = false; 40 if (command.GetArgumentCount() == 0) { 41 Thread *thread = m_exe_ctx.GetThreadPtr(); 42 if (thread) 43 HandleOneThread(thread->GetID(), result); 44 return; 45 } else if (command.GetArgumentCount() == 1) { 46 all_threads = ::strcmp(command.GetArgumentAtIndex(0), "all") == 0; 47 m_unique_stacks = ::strcmp(command.GetArgumentAtIndex(0), "unique") == 0; 48 } 49 50 // Use tids instead of ThreadSPs to prevent deadlocking problems which 51 // result from JIT-ing code while iterating over the (locked) ThreadSP 52 // list. 53 std::vector<lldb::tid_t> tids; 54 55 if (all_threads || m_unique_stacks) { 56 Process *process = m_exe_ctx.GetProcessPtr(); 57 58 for (ThreadSP thread_sp : process->Threads()) 59 tids.push_back(thread_sp->GetID()); 60 } else { 61 const size_t num_args = command.GetArgumentCount(); 62 Process *process = m_exe_ctx.GetProcessPtr(); 63 64 std::lock_guard<std::recursive_mutex> guard( 65 process->GetThreadList().GetMutex()); 66 67 for (size_t i = 0; i < num_args; i++) { 68 uint32_t thread_idx; 69 if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) { 70 result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n", 71 command.GetArgumentAtIndex(i)); 72 return; 73 } 74 75 ThreadSP thread = 76 process->GetThreadList().FindThreadByIndexID(thread_idx); 77 78 if (!thread) { 79 result.AppendErrorWithFormat("no thread with index: \"%s\"\n", 80 command.GetArgumentAtIndex(i)); 81 return; 82 } 83 84 tids.push_back(thread->GetID()); 85 } 86 } 87 88 if (m_unique_stacks) { 89 // Iterate over threads, finding unique stack buckets. 90 std::set<UniqueStack> unique_stacks; 91 for (const lldb::tid_t &tid : tids) { 92 if (!BucketThread(tid, unique_stacks, result)) { 93 return; 94 } 95 } 96 97 // Write the thread id's and unique call stacks to the output stream 98 Stream &strm = result.GetOutputStream(); 99 Process *process = m_exe_ctx.GetProcessPtr(); 100 for (const UniqueStack &stack : unique_stacks) { 101 // List the common thread ID's 102 const std::vector<uint32_t> &thread_index_ids = 103 stack.GetUniqueThreadIndexIDs(); 104 strm.Format("{0} thread(s) ", thread_index_ids.size()); 105 for (const uint32_t &thread_index_id : thread_index_ids) { 106 strm.Format("#{0} ", thread_index_id); 107 } 108 strm.EOL(); 109 110 // List the shared call stack for this set of threads 111 uint32_t representative_thread_id = stack.GetRepresentativeThread(); 112 ThreadSP thread = process->GetThreadList().FindThreadByIndexID( 113 representative_thread_id); 114 if (!HandleOneThread(thread->GetID(), result)) { 115 return; 116 } 117 } 118 } else { 119 uint32_t idx = 0; 120 for (const lldb::tid_t &tid : tids) { 121 if (idx != 0 && m_add_return) 122 result.AppendMessage(""); 123 124 if (!HandleOneThread(tid, result)) 125 return; 126 127 ++idx; 128 } 129 } 130 } 131 132 bool CommandObjectIterateOverThreads::BucketThread( 133 lldb::tid_t tid, std::set<UniqueStack> &unique_stacks, 134 CommandReturnObject &result) { 135 // Grab the corresponding thread for the given thread id. 136 Process *process = m_exe_ctx.GetProcessPtr(); 137 Thread *thread = process->GetThreadList().FindThreadByID(tid).get(); 138 if (thread == nullptr) { 139 result.AppendErrorWithFormatv("Failed to process thread #{0}.\n", tid); 140 return false; 141 } 142 143 // Collect the each frame's address for this call-stack 144 std::stack<lldb::addr_t> stack_frames; 145 const uint32_t frame_count = thread->GetStackFrameCount(); 146 for (uint32_t frame_index = 0; frame_index < frame_count; frame_index++) { 147 const lldb::StackFrameSP frame_sp = 148 thread->GetStackFrameAtIndex(frame_index); 149 const lldb::addr_t pc = frame_sp->GetStackID().GetPC(); 150 stack_frames.push(pc); 151 } 152 153 uint32_t thread_index_id = thread->GetIndexID(); 154 UniqueStack new_unique_stack(stack_frames, thread_index_id); 155 156 // Try to match the threads stack to and existing entry. 157 std::set<UniqueStack>::iterator matching_stack = 158 unique_stacks.find(new_unique_stack); 159 if (matching_stack != unique_stacks.end()) { 160 matching_stack->AddThread(thread_index_id); 161 } else { 162 unique_stacks.insert(new_unique_stack); 163 } 164 return true; 165 } 166 167 void CommandObjectMultipleThreads::DoExecute(Args &command, 168 CommandReturnObject &result) { 169 Process &process = m_exe_ctx.GetProcessRef(); 170 171 std::vector<lldb::tid_t> tids; 172 const size_t num_args = command.GetArgumentCount(); 173 174 std::lock_guard<std::recursive_mutex> guard( 175 process.GetThreadList().GetMutex()); 176 177 if (num_args > 0 && ::strcmp(command.GetArgumentAtIndex(0), "all") == 0) { 178 for (ThreadSP thread_sp : process.Threads()) 179 tids.push_back(thread_sp->GetID()); 180 } else { 181 if (num_args == 0) { 182 Thread &thread = m_exe_ctx.GetThreadRef(); 183 tids.push_back(thread.GetID()); 184 } 185 186 for (size_t i = 0; i < num_args; i++) { 187 uint32_t thread_idx; 188 if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) { 189 result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n", 190 command.GetArgumentAtIndex(i)); 191 return; 192 } 193 194 ThreadSP thread = process.GetThreadList().FindThreadByIndexID(thread_idx); 195 196 if (!thread) { 197 result.AppendErrorWithFormat("no thread with index: \"%s\"\n", 198 command.GetArgumentAtIndex(i)); 199 return; 200 } 201 202 tids.push_back(thread->GetID()); 203 } 204 } 205 206 DoExecuteOnThreads(command, result, tids); 207 } 208