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