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