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