1 //===-- CommandObjectThreadUtil.cpp -----------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "CommandObjectThreadUtil.h"
11 #include "lldb/Interpreter/CommandReturnObject.h"
12 #include "lldb/Target/Process.h"
13 #include "lldb/Target/Thread.h"
16 using namespace lldb_private
;
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
});
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
});
37 void CommandObjectIterateOverThreads::DoExecute(Args
&command
,
38 CommandReturnObject
&result
) {
39 result
.SetStatus(m_success_return
);
41 bool all_threads
= false;
42 if (command
.GetArgumentCount() == 0) {
43 Thread
*thread
= m_exe_ctx
.GetThreadPtr();
45 HandleOneThread(thread
->GetID(), result
);
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;
52 // Use tids instead of ThreadSPs to prevent deadlocking problems which
53 // result from JIT-ing code while iterating over the (locked) ThreadSP
55 std::vector
<lldb::tid_t
> tids
;
57 if (all_threads
|| m_unique_stacks
) {
58 Process
*process
= m_exe_ctx
.GetProcessPtr();
60 for (ThreadSP thread_sp
: process
->Threads())
61 tids
.push_back(thread_sp
->GetID());
63 const size_t num_args
= command
.GetArgumentCount();
64 Process
*process
= m_exe_ctx
.GetProcessPtr();
66 std::lock_guard
<std::recursive_mutex
> guard(
67 process
->GetThreadList().GetMutex());
69 for (size_t i
= 0; i
< num_args
; i
++) {
71 if (!llvm::to_integer(command
.GetArgumentAtIndex(i
), thread_idx
)) {
72 result
.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
73 command
.GetArgumentAtIndex(i
));
78 process
->GetThreadList().FindThreadByIndexID(thread_idx
);
81 result
.AppendErrorWithFormat("no thread with index: \"%s\"\n",
82 command
.GetArgumentAtIndex(i
));
86 tids
.push_back(thread
->GetID());
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
)) {
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
);
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
)) {
122 for (const lldb::tid_t
&tid
: tids
) {
123 if (idx
!= 0 && m_add_return
)
124 result
.AppendMessage("");
126 if (!HandleOneThread(tid
, result
))
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
);
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
);
155 uint32_t thread_index_id
= thread
->GetIndexID();
156 UniqueStack
new_unique_stack(stack_frames
, thread_index_id
);
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
);
164 unique_stacks
.insert(new_unique_stack
);
169 void CommandObjectMultipleThreads::DoExecute(Args
&command
,
170 CommandReturnObject
&result
) {
171 Process
&process
= m_exe_ctx
.GetProcessRef();
173 std::vector
<lldb::tid_t
> tids
;
174 const size_t num_args
= command
.GetArgumentCount();
176 std::lock_guard
<std::recursive_mutex
> guard(
177 process
.GetThreadList().GetMutex());
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());
184 Thread
&thread
= m_exe_ctx
.GetThreadRef();
185 tids
.push_back(thread
.GetID());
188 for (size_t i
= 0; i
< num_args
; i
++) {
190 if (!llvm::to_integer(command
.GetArgumentAtIndex(i
), thread_idx
)) {
191 result
.AppendErrorWithFormat("invalid thread specification: \"%s\"\n",
192 command
.GetArgumentAtIndex(i
));
196 ThreadSP thread
= process
.GetThreadList().FindThreadByIndexID(thread_idx
);
199 result
.AppendErrorWithFormat("no thread with index: \"%s\"\n",
200 command
.GetArgumentAtIndex(i
));
204 tids
.push_back(thread
->GetID());
208 DoExecuteOnThreads(command
, result
, tids
);