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({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({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(command.GetArgumentAtIndex(0), "all") == 0;
49     m_unique_stacks = ::strcmp(command.GetArgumentAtIndex(0), "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(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(command.GetArgumentAtIndex(i), thread_idx)) {
72         result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
73                                      command.GetArgumentAtIndex(i));
74         return;
75       }
76 
77       ThreadSP thread =
78           process->GetThreadList().FindThreadByIndexID(thread_idx);
79 
80       if (!thread) {
81         result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
82                                      command.GetArgumentAtIndex(i));
83         return;
84       }
85 
86       tids.push_back(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("{0} thread(s) ", thread_index_ids.size());
107       for (const uint32_t &thread_index_id : thread_index_ids) {
108         strm.Format("#{0} ", 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           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("");
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("Failed to process thread #{0}.\n", 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(frame_index);
151     const lldb::addr_t pc = frame_sp->GetStackID().GetPC();
152     stack_frames.push(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(new_unique_stack);
161   if (matching_stack != unique_stacks.end()) {
162     matching_stack->AddThread(thread_index_id);
163   } else {
164     unique_stacks.insert(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(command.GetArgumentAtIndex(0), "all") == 0) {
180     for (ThreadSP thread_sp : process.Threads())
181       tids.push_back(thread_sp->GetID());
182   } else {
183     if (num_args == 0) {
184       Thread &thread = m_exe_ctx.GetThreadRef();
185       tids.push_back(thread.GetID());
186     }
187 
188     for (size_t i = 0; i < num_args; i++) {
189       uint32_t thread_idx;
190       if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) {
191         result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
192                                      command.GetArgumentAtIndex(i));
193         return;
194       }
195 
196       ThreadSP thread = process.GetThreadList().FindThreadByIndexID(thread_idx);
197 
198       if (!thread) {
199         result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
200                                      command.GetArgumentAtIndex(i));
201         return;
202       }
203 
204       tids.push_back(thread->GetID());
205     }
206   }
207 
208   DoExecuteOnThreads(command, result, tids);
209 }
210