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