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