xref: /freebsd-src/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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