1 //===-- ScriptedThread.cpp ------------------------------------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "ScriptedThread.h"
10 
11 #include "Plugins/Process/Utility/RegisterContextThreadMemory.h"
12 #include "lldb/Target/OperatingSystem.h"
13 #include "lldb/Target/Process.h"
14 #include "lldb/Target/RegisterContext.h"
15 #include "lldb/Target/StopInfo.h"
16 #include "lldb/Target/Unwind.h"
17 #include "lldb/Utility/DataBufferHeap.h"
18 #include "lldb/Utility/Log.h"
19 #include "lldb/Utility/Logging.h"
20 
21 #include <memory>
22 
23 using namespace lldb;
24 using namespace lldb_private;
25 
26 void ScriptedThread::CheckInterpreterAndScriptObject() const {
27   lldbassert(m_script_object_sp && "Invalid Script Object.");
28   lldbassert(GetInterface() && "Invalid Scripted Thread Interface.");
29 }
30 
31 ScriptedThread::ScriptedThread(ScriptedProcess &process, Status &error,
32                                StructuredData::Generic *script_object)
33     : Thread(process, LLDB_INVALID_THREAD_ID), m_scripted_process(process),
34       m_scripted_thread_interface_sp(
35           m_scripted_process.GetInterface().CreateScriptedThreadInterface()) {
36   if (!process.IsValid()) {
37     error.SetErrorString("Invalid scripted process");
38     return;
39   }
40 
41   process.CheckInterpreterAndScriptObject();
42 
43   auto scripted_thread_interface = GetInterface();
44   if (!scripted_thread_interface) {
45     error.SetErrorString("Failed to get scripted thread interface.");
46     return;
47   }
48 
49   llvm::Optional<std::string> class_name =
50       process.GetInterface().GetScriptedThreadPluginName();
51   if (!class_name || class_name->empty()) {
52     error.SetErrorString("Failed to get scripted thread class name.");
53     return;
54   }
55 
56   ExecutionContext exe_ctx(process);
57 
58   m_script_object_sp = scripted_thread_interface->CreatePluginObject(
59       class_name->c_str(), exe_ctx, process.m_scripted_process_info.GetArgsSP(),
60       script_object);
61 
62   if (!m_script_object_sp) {
63     error.SetErrorString("Failed to create script object");
64     return;
65   }
66 
67   if (!m_script_object_sp->IsValid()) {
68     m_script_object_sp = nullptr;
69     error.SetErrorString("Created script object is invalid");
70     return;
71   }
72 
73   lldb::tid_t tid = scripted_thread_interface->GetThreadID();
74   SetID(tid);
75 }
76 
77 ScriptedThread::~ScriptedThread() { DestroyThread(); }
78 
79 const char *ScriptedThread::GetName() {
80   CheckInterpreterAndScriptObject();
81   llvm::Optional<std::string> thread_name = GetInterface()->GetName();
82   if (!thread_name)
83     return nullptr;
84   return ConstString(thread_name->c_str()).AsCString();
85 }
86 
87 const char *ScriptedThread::GetQueueName() {
88   CheckInterpreterAndScriptObject();
89   llvm::Optional<std::string> queue_name = GetInterface()->GetQueue();
90   if (!queue_name)
91     return nullptr;
92   return ConstString(queue_name->c_str()).AsCString();
93 }
94 
95 void ScriptedThread::WillResume(StateType resume_state) {}
96 
97 void ScriptedThread::ClearStackFrames() { Thread::ClearStackFrames(); }
98 
99 RegisterContextSP ScriptedThread::GetRegisterContext() {
100   if (!m_reg_context_sp)
101     m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
102   return m_reg_context_sp;
103 }
104 
105 RegisterContextSP
106 ScriptedThread::CreateRegisterContextForFrame(StackFrame *frame) {
107   const uint32_t concrete_frame_idx =
108       frame ? frame->GetConcreteFrameIndex() : 0;
109 
110   if (concrete_frame_idx)
111     return GetUnwinder().CreateRegisterContextForFrame(frame);
112 
113   lldb::RegisterContextSP reg_ctx_sp;
114   Status error;
115 
116   llvm::Optional<std::string> reg_data = GetInterface()->GetRegisterContext();
117   if (!reg_data)
118     return GetInterface()->ErrorWithMessage<lldb::RegisterContextSP>(
119         LLVM_PRETTY_FUNCTION, "Failed to get scripted thread registers data.",
120         error, LIBLLDB_LOG_THREAD);
121 
122   DataBufferSP data_sp(
123       std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size()));
124 
125   if (!data_sp->GetByteSize())
126     return GetInterface()->ErrorWithMessage<lldb::RegisterContextSP>(
127         LLVM_PRETTY_FUNCTION, "Failed to copy raw registers data.", error,
128         LIBLLDB_LOG_THREAD);
129 
130   std::shared_ptr<RegisterContextMemory> reg_ctx_memory =
131       std::make_shared<RegisterContextMemory>(
132           *this, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS);
133   if (!reg_ctx_memory)
134     return GetInterface()->ErrorWithMessage<lldb::RegisterContextSP>(
135         LLVM_PRETTY_FUNCTION, "Failed to create a register context.", error,
136         LIBLLDB_LOG_THREAD);
137 
138   reg_ctx_memory->SetAllRegisterData(data_sp);
139   m_reg_context_sp = reg_ctx_memory;
140 
141   return m_reg_context_sp;
142 }
143 
144 bool ScriptedThread::CalculateStopInfo() {
145   StructuredData::DictionarySP dict_sp = GetInterface()->GetStopReason();
146 
147   Status error;
148   if (!dict_sp)
149     return GetInterface()->ErrorWithMessage<bool>(
150         LLVM_PRETTY_FUNCTION, "Failed to get scripted thread stop info.", error,
151         LIBLLDB_LOG_THREAD);
152 
153   lldb::StopInfoSP stop_info_sp;
154   lldb::StopReason stop_reason_type;
155 
156   if (!dict_sp->GetValueForKeyAsInteger("type", stop_reason_type))
157     return GetInterface()->ErrorWithMessage<bool>(
158         LLVM_PRETTY_FUNCTION,
159         "Couldn't find value for key 'type' in stop reason dictionary.", error,
160         LIBLLDB_LOG_THREAD);
161 
162   StructuredData::Dictionary *data_dict;
163   if (!dict_sp->GetValueForKeyAsDictionary("data", data_dict))
164     return GetInterface()->ErrorWithMessage<bool>(
165         LLVM_PRETTY_FUNCTION,
166         "Couldn't find value for key 'data' in stop reason dictionary.", error,
167         LIBLLDB_LOG_THREAD);
168 
169   switch (stop_reason_type) {
170   case lldb::eStopReasonNone:
171     return true;
172   case lldb::eStopReasonBreakpoint: {
173     lldb::break_id_t break_id;
174     data_dict->GetValueForKeyAsInteger("break_id", break_id,
175                                        LLDB_INVALID_BREAK_ID);
176     stop_info_sp =
177         StopInfo::CreateStopReasonWithBreakpointSiteID(*this, break_id);
178   } break;
179   case lldb::eStopReasonSignal: {
180     int signal;
181     llvm::StringRef description;
182     data_dict->GetValueForKeyAsInteger("signal", signal,
183                                        LLDB_INVALID_SIGNAL_NUMBER);
184     data_dict->GetValueForKeyAsString("desc", description);
185     stop_info_sp =
186         StopInfo::CreateStopReasonWithSignal(*this, signal, description.data());
187   } break;
188   case lldb::eStopReasonException: {
189     llvm::StringRef description;
190     data_dict->GetValueForKeyAsString("desc", description);
191 
192     stop_info_sp =
193         StopInfo::CreateStopReasonWithException(*this, description.data());
194   } break;
195   default:
196     return GetInterface()->ErrorWithMessage<bool>(
197         LLVM_PRETTY_FUNCTION,
198         llvm::Twine("Unsupported stop reason type (" +
199                     llvm::Twine(stop_reason_type) + llvm::Twine(")."))
200             .str(),
201         error, LIBLLDB_LOG_THREAD);
202   }
203 
204   if (!stop_info_sp)
205     return false;
206 
207   SetStopInfo(stop_info_sp);
208   return true;
209 }
210 
211 void ScriptedThread::RefreshStateAfterStop() {
212   GetRegisterContext()->InvalidateIfNeeded(/*force=*/false);
213 }
214 
215 lldb::ScriptedThreadInterfaceSP ScriptedThread::GetInterface() const {
216   return m_scripted_thread_interface_sp;
217 }
218 
219 std::shared_ptr<DynamicRegisterInfo> ScriptedThread::GetDynamicRegisterInfo() {
220   CheckInterpreterAndScriptObject();
221 
222   if (!m_register_info_sp) {
223     StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo();
224 
225     Status error;
226     if (!reg_info)
227       return GetInterface()
228           ->ErrorWithMessage<std::shared_ptr<DynamicRegisterInfo>>(
229               LLVM_PRETTY_FUNCTION,
230               "Failed to get scripted thread registers info.", error,
231               LIBLLDB_LOG_THREAD);
232 
233     m_register_info_sp = std::make_shared<DynamicRegisterInfo>(
234         *reg_info, m_scripted_process.GetTarget().GetArchitecture());
235   }
236 
237   return m_register_info_sp;
238 }
239