xref: /src/contrib/llvm-project/lldb/source/Commands/CommandObjectThreadUtil.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1b60736ecSDimitry Andric //===-- CommandObjectThreadUtil.cpp -----------------------------*- C++ -*-===//
2b60736ecSDimitry Andric //
3b60736ecSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b60736ecSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5b60736ecSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b60736ecSDimitry Andric //
7b60736ecSDimitry Andric //===----------------------------------------------------------------------===//
8b60736ecSDimitry Andric 
9b60736ecSDimitry Andric #include "CommandObjectThreadUtil.h"
10b60736ecSDimitry Andric 
11b60736ecSDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h"
12b60736ecSDimitry Andric #include "lldb/Target/Process.h"
13b60736ecSDimitry Andric #include "lldb/Target/Thread.h"
14b60736ecSDimitry Andric 
15b60736ecSDimitry Andric using namespace lldb;
16b60736ecSDimitry Andric using namespace lldb_private;
17b60736ecSDimitry Andric using namespace llvm;
18b60736ecSDimitry Andric 
CommandObjectIterateOverThreads(CommandInterpreter & interpreter,const char * name,const char * help,const char * syntax,uint32_t flags)19b60736ecSDimitry Andric CommandObjectIterateOverThreads::CommandObjectIterateOverThreads(
20b60736ecSDimitry Andric     CommandInterpreter &interpreter, const char *name, const char *help,
21b60736ecSDimitry Andric     const char *syntax, uint32_t flags)
22145449b1SDimitry Andric     : CommandObjectParsed(interpreter, name, help, syntax, flags) {
23145449b1SDimitry Andric   // These commands all take thread ID's as arguments.
24ac9a064cSDimitry Andric   AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatStar);
25145449b1SDimitry Andric }
26145449b1SDimitry Andric 
CommandObjectMultipleThreads(CommandInterpreter & interpreter,const char * name,const char * help,const char * syntax,uint32_t flags)27145449b1SDimitry Andric CommandObjectMultipleThreads::CommandObjectMultipleThreads(
28145449b1SDimitry Andric     CommandInterpreter &interpreter, const char *name, const char *help,
29145449b1SDimitry Andric     const char *syntax, uint32_t flags)
30145449b1SDimitry Andric     : CommandObjectParsed(interpreter, name, help, syntax, flags) {
31145449b1SDimitry Andric   // These commands all take thread ID's as arguments.
32ac9a064cSDimitry Andric   AddSimpleArgumentList(eArgTypeThreadIndex, eArgRepeatStar);
33145449b1SDimitry Andric }
34b60736ecSDimitry Andric 
DoExecute(Args & command,CommandReturnObject & result)35b1c73532SDimitry Andric void CommandObjectIterateOverThreads::DoExecute(Args &command,
36b60736ecSDimitry Andric                                                 CommandReturnObject &result) {
37b60736ecSDimitry Andric   result.SetStatus(m_success_return);
38b60736ecSDimitry Andric 
39b60736ecSDimitry Andric   bool all_threads = false;
40b60736ecSDimitry Andric   if (command.GetArgumentCount() == 0) {
41b60736ecSDimitry Andric     Thread *thread = m_exe_ctx.GetThreadPtr();
42b1c73532SDimitry Andric     if (thread)
43b1c73532SDimitry Andric       HandleOneThread(thread->GetID(), result);
44b1c73532SDimitry Andric     return;
45b60736ecSDimitry Andric   } else if (command.GetArgumentCount() == 1) {
46b60736ecSDimitry Andric     all_threads = ::strcmp(command.GetArgumentAtIndex(0), "all") == 0;
47b60736ecSDimitry Andric     m_unique_stacks = ::strcmp(command.GetArgumentAtIndex(0), "unique") == 0;
48b60736ecSDimitry Andric   }
49b60736ecSDimitry Andric 
50b60736ecSDimitry Andric   // Use tids instead of ThreadSPs to prevent deadlocking problems which
51b60736ecSDimitry Andric   // result from JIT-ing code while iterating over the (locked) ThreadSP
52b60736ecSDimitry Andric   // list.
53b60736ecSDimitry Andric   std::vector<lldb::tid_t> tids;
54b60736ecSDimitry Andric 
55b60736ecSDimitry Andric   if (all_threads || m_unique_stacks) {
56b60736ecSDimitry Andric     Process *process = m_exe_ctx.GetProcessPtr();
57b60736ecSDimitry Andric 
58b60736ecSDimitry Andric     for (ThreadSP thread_sp : process->Threads())
59b60736ecSDimitry Andric       tids.push_back(thread_sp->GetID());
60b60736ecSDimitry Andric   } else {
61b60736ecSDimitry Andric     const size_t num_args = command.GetArgumentCount();
62b60736ecSDimitry Andric     Process *process = m_exe_ctx.GetProcessPtr();
63b60736ecSDimitry Andric 
64b60736ecSDimitry Andric     std::lock_guard<std::recursive_mutex> guard(
65b60736ecSDimitry Andric         process->GetThreadList().GetMutex());
66b60736ecSDimitry Andric 
67b60736ecSDimitry Andric     for (size_t i = 0; i < num_args; i++) {
68b60736ecSDimitry Andric       uint32_t thread_idx;
69b60736ecSDimitry Andric       if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) {
70b60736ecSDimitry Andric         result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
71b60736ecSDimitry Andric                                      command.GetArgumentAtIndex(i));
72b1c73532SDimitry Andric         return;
73b60736ecSDimitry Andric       }
74b60736ecSDimitry Andric 
75b60736ecSDimitry Andric       ThreadSP thread =
76b60736ecSDimitry Andric           process->GetThreadList().FindThreadByIndexID(thread_idx);
77b60736ecSDimitry Andric 
78b60736ecSDimitry Andric       if (!thread) {
79b60736ecSDimitry Andric         result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
80b60736ecSDimitry Andric                                      command.GetArgumentAtIndex(i));
81b1c73532SDimitry Andric         return;
82b60736ecSDimitry Andric       }
83b60736ecSDimitry Andric 
84b60736ecSDimitry Andric       tids.push_back(thread->GetID());
85b60736ecSDimitry Andric     }
86b60736ecSDimitry Andric   }
87b60736ecSDimitry Andric 
88b60736ecSDimitry Andric   if (m_unique_stacks) {
89b60736ecSDimitry Andric     // Iterate over threads, finding unique stack buckets.
90b60736ecSDimitry Andric     std::set<UniqueStack> unique_stacks;
91b60736ecSDimitry Andric     for (const lldb::tid_t &tid : tids) {
92b60736ecSDimitry Andric       if (!BucketThread(tid, unique_stacks, result)) {
93b1c73532SDimitry Andric         return;
94b60736ecSDimitry Andric       }
95b60736ecSDimitry Andric     }
96b60736ecSDimitry Andric 
97b60736ecSDimitry Andric     // Write the thread id's and unique call stacks to the output stream
98b60736ecSDimitry Andric     Stream &strm = result.GetOutputStream();
99b60736ecSDimitry Andric     Process *process = m_exe_ctx.GetProcessPtr();
100b60736ecSDimitry Andric     for (const UniqueStack &stack : unique_stacks) {
101b60736ecSDimitry Andric       // List the common thread ID's
102b60736ecSDimitry Andric       const std::vector<uint32_t> &thread_index_ids =
103b60736ecSDimitry Andric           stack.GetUniqueThreadIndexIDs();
104b60736ecSDimitry Andric       strm.Format("{0} thread(s) ", thread_index_ids.size());
105b60736ecSDimitry Andric       for (const uint32_t &thread_index_id : thread_index_ids) {
106b60736ecSDimitry Andric         strm.Format("#{0} ", thread_index_id);
107b60736ecSDimitry Andric       }
108b60736ecSDimitry Andric       strm.EOL();
109b60736ecSDimitry Andric 
110b60736ecSDimitry Andric       // List the shared call stack for this set of threads
111b60736ecSDimitry Andric       uint32_t representative_thread_id = stack.GetRepresentativeThread();
112b60736ecSDimitry Andric       ThreadSP thread = process->GetThreadList().FindThreadByIndexID(
113b60736ecSDimitry Andric           representative_thread_id);
114b60736ecSDimitry Andric       if (!HandleOneThread(thread->GetID(), result)) {
115b1c73532SDimitry Andric         return;
116b60736ecSDimitry Andric       }
117b60736ecSDimitry Andric     }
118b60736ecSDimitry Andric   } else {
119b60736ecSDimitry Andric     uint32_t idx = 0;
120b60736ecSDimitry Andric     for (const lldb::tid_t &tid : tids) {
121b60736ecSDimitry Andric       if (idx != 0 && m_add_return)
122b60736ecSDimitry Andric         result.AppendMessage("");
123b60736ecSDimitry Andric 
124b60736ecSDimitry Andric       if (!HandleOneThread(tid, result))
125b1c73532SDimitry Andric         return;
126b60736ecSDimitry Andric 
127b60736ecSDimitry Andric       ++idx;
128b60736ecSDimitry Andric     }
129b60736ecSDimitry Andric   }
130b60736ecSDimitry Andric }
131b60736ecSDimitry Andric 
BucketThread(lldb::tid_t tid,std::set<UniqueStack> & unique_stacks,CommandReturnObject & result)132b60736ecSDimitry Andric bool CommandObjectIterateOverThreads::BucketThread(
133b60736ecSDimitry Andric     lldb::tid_t tid, std::set<UniqueStack> &unique_stacks,
134b60736ecSDimitry Andric     CommandReturnObject &result) {
135b60736ecSDimitry Andric   // Grab the corresponding thread for the given thread id.
136b60736ecSDimitry Andric   Process *process = m_exe_ctx.GetProcessPtr();
137b60736ecSDimitry Andric   Thread *thread = process->GetThreadList().FindThreadByID(tid).get();
138b60736ecSDimitry Andric   if (thread == nullptr) {
139b60736ecSDimitry Andric     result.AppendErrorWithFormatv("Failed to process thread #{0}.\n", tid);
140b60736ecSDimitry Andric     return false;
141b60736ecSDimitry Andric   }
142b60736ecSDimitry Andric 
143b60736ecSDimitry Andric   // Collect the each frame's address for this call-stack
144b60736ecSDimitry Andric   std::stack<lldb::addr_t> stack_frames;
145b60736ecSDimitry Andric   const uint32_t frame_count = thread->GetStackFrameCount();
146b60736ecSDimitry Andric   for (uint32_t frame_index = 0; frame_index < frame_count; frame_index++) {
147b60736ecSDimitry Andric     const lldb::StackFrameSP frame_sp =
148b60736ecSDimitry Andric         thread->GetStackFrameAtIndex(frame_index);
149b60736ecSDimitry Andric     const lldb::addr_t pc = frame_sp->GetStackID().GetPC();
150b60736ecSDimitry Andric     stack_frames.push(pc);
151b60736ecSDimitry Andric   }
152b60736ecSDimitry Andric 
153b60736ecSDimitry Andric   uint32_t thread_index_id = thread->GetIndexID();
154b60736ecSDimitry Andric   UniqueStack new_unique_stack(stack_frames, thread_index_id);
155b60736ecSDimitry Andric 
156b60736ecSDimitry Andric   // Try to match the threads stack to and existing entry.
157b60736ecSDimitry Andric   std::set<UniqueStack>::iterator matching_stack =
158b60736ecSDimitry Andric       unique_stacks.find(new_unique_stack);
159b60736ecSDimitry Andric   if (matching_stack != unique_stacks.end()) {
160b60736ecSDimitry Andric     matching_stack->AddThread(thread_index_id);
161b60736ecSDimitry Andric   } else {
162b60736ecSDimitry Andric     unique_stacks.insert(new_unique_stack);
163b60736ecSDimitry Andric   }
164b60736ecSDimitry Andric   return true;
165b60736ecSDimitry Andric }
166344a3780SDimitry Andric 
DoExecute(Args & command,CommandReturnObject & result)167b1c73532SDimitry Andric void CommandObjectMultipleThreads::DoExecute(Args &command,
168344a3780SDimitry Andric                                              CommandReturnObject &result) {
169344a3780SDimitry Andric   Process &process = m_exe_ctx.GetProcessRef();
170344a3780SDimitry Andric 
171344a3780SDimitry Andric   std::vector<lldb::tid_t> tids;
172344a3780SDimitry Andric   const size_t num_args = command.GetArgumentCount();
173344a3780SDimitry Andric 
174344a3780SDimitry Andric   std::lock_guard<std::recursive_mutex> guard(
175344a3780SDimitry Andric       process.GetThreadList().GetMutex());
176344a3780SDimitry Andric 
177344a3780SDimitry Andric   if (num_args > 0 && ::strcmp(command.GetArgumentAtIndex(0), "all") == 0) {
178344a3780SDimitry Andric     for (ThreadSP thread_sp : process.Threads())
179344a3780SDimitry Andric       tids.push_back(thread_sp->GetID());
180344a3780SDimitry Andric   } else {
181344a3780SDimitry Andric     if (num_args == 0) {
182344a3780SDimitry Andric       Thread &thread = m_exe_ctx.GetThreadRef();
183344a3780SDimitry Andric       tids.push_back(thread.GetID());
184344a3780SDimitry Andric     }
185344a3780SDimitry Andric 
186344a3780SDimitry Andric     for (size_t i = 0; i < num_args; i++) {
187344a3780SDimitry Andric       uint32_t thread_idx;
188344a3780SDimitry Andric       if (!llvm::to_integer(command.GetArgumentAtIndex(i), thread_idx)) {
189344a3780SDimitry Andric         result.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
190344a3780SDimitry Andric                                      command.GetArgumentAtIndex(i));
191b1c73532SDimitry Andric         return;
192344a3780SDimitry Andric       }
193344a3780SDimitry Andric 
194344a3780SDimitry Andric       ThreadSP thread = process.GetThreadList().FindThreadByIndexID(thread_idx);
195344a3780SDimitry Andric 
196344a3780SDimitry Andric       if (!thread) {
197344a3780SDimitry Andric         result.AppendErrorWithFormat("no thread with index: \"%s\"\n",
198344a3780SDimitry Andric                                      command.GetArgumentAtIndex(i));
199b1c73532SDimitry Andric         return;
200344a3780SDimitry Andric       }
201344a3780SDimitry Andric 
202344a3780SDimitry Andric       tids.push_back(thread->GetID());
203344a3780SDimitry Andric     }
204344a3780SDimitry Andric   }
205344a3780SDimitry Andric 
206b1c73532SDimitry Andric   DoExecuteOnThreads(command, result, tids);
207344a3780SDimitry Andric }
208