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 "Plugins/Process/Utility/StopInfoMachException.h"
13 #include "lldb/Target/OperatingSystem.h"
14 #include "lldb/Target/Process.h"
15 #include "lldb/Target/RegisterContext.h"
16 #include "lldb/Target/StopInfo.h"
17 #include "lldb/Target/Unwind.h"
18 #include "lldb/Utility/DataBufferHeap.h"
19 #include "lldb/Utility/LLDBLog.h"
20 #include <memory>
21 #include <optional>
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 llvm::Expected<std::shared_ptr<ScriptedThread>>
32 ScriptedThread::Create(ScriptedProcess &process,
33                        StructuredData::Generic *script_object) {
34   if (!process.IsValid())
35     return llvm::createStringError(llvm::inconvertibleErrorCode(),
36                                    "Invalid scripted process.");
37 
38   process.CheckInterpreterAndScriptObject();
39 
40   auto scripted_thread_interface =
41       process.GetInterface().CreateScriptedThreadInterface();
42   if (!scripted_thread_interface)
43     return llvm::createStringError(
44         llvm::inconvertibleErrorCode(),
45         "Failed to create scripted thread interface.");
46 
47   llvm::StringRef thread_class_name;
48   if (!script_object) {
49     std::optional<std::string> class_name =
50         process.GetInterface().GetScriptedThreadPluginName();
51     if (!class_name || class_name->empty())
52       return llvm::createStringError(
53           llvm::inconvertibleErrorCode(),
54           "Failed to get scripted thread class name.");
55     thread_class_name = *class_name;
56   }
57 
58   ExecutionContext exe_ctx(process);
59   StructuredData::GenericSP owned_script_object_sp =
60       scripted_thread_interface->CreatePluginObject(
61           thread_class_name, exe_ctx, process.m_scripted_metadata.GetArgsSP(),
62           script_object);
63 
64   if (!owned_script_object_sp)
65     return llvm::createStringError(llvm::inconvertibleErrorCode(),
66                                    "Failed to create script object.");
67   if (!owned_script_object_sp->IsValid())
68     return llvm::createStringError(llvm::inconvertibleErrorCode(),
69                                    "Created script object is invalid.");
70 
71   lldb::tid_t tid = scripted_thread_interface->GetThreadID();
72 
73   return std::make_shared<ScriptedThread>(process, scripted_thread_interface,
74                                           tid, owned_script_object_sp);
75 }
76 
77 ScriptedThread::ScriptedThread(ScriptedProcess &process,
78                                ScriptedThreadInterfaceSP interface_sp,
79                                lldb::tid_t tid,
80                                StructuredData::GenericSP script_object_sp)
81     : Thread(process, tid), m_scripted_process(process),
82       m_scripted_thread_interface_sp(interface_sp),
83       m_script_object_sp(script_object_sp) {}
84 
85 ScriptedThread::~ScriptedThread() { DestroyThread(); }
86 
87 const char *ScriptedThread::GetName() {
88   CheckInterpreterAndScriptObject();
89   std::optional<std::string> thread_name = GetInterface()->GetName();
90   if (!thread_name)
91     return nullptr;
92   return ConstString(thread_name->c_str()).AsCString();
93 }
94 
95 const char *ScriptedThread::GetQueueName() {
96   CheckInterpreterAndScriptObject();
97   std::optional<std::string> queue_name = GetInterface()->GetQueue();
98   if (!queue_name)
99     return nullptr;
100   return ConstString(queue_name->c_str()).AsCString();
101 }
102 
103 void ScriptedThread::WillResume(StateType resume_state) {}
104 
105 void ScriptedThread::ClearStackFrames() { Thread::ClearStackFrames(); }
106 
107 RegisterContextSP ScriptedThread::GetRegisterContext() {
108   if (!m_reg_context_sp)
109     m_reg_context_sp = CreateRegisterContextForFrame(nullptr);
110   return m_reg_context_sp;
111 }
112 
113 RegisterContextSP
114 ScriptedThread::CreateRegisterContextForFrame(StackFrame *frame) {
115   const uint32_t concrete_frame_idx =
116       frame ? frame->GetConcreteFrameIndex() : 0;
117 
118   if (concrete_frame_idx)
119     return GetUnwinder().CreateRegisterContextForFrame(frame);
120 
121   lldb::RegisterContextSP reg_ctx_sp;
122   Status error;
123 
124   std::optional<std::string> reg_data = GetInterface()->GetRegisterContext();
125   if (!reg_data)
126     return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>(
127         LLVM_PRETTY_FUNCTION, "Failed to get scripted thread registers data.",
128         error, LLDBLog::Thread);
129 
130   DataBufferSP data_sp(
131       std::make_shared<DataBufferHeap>(reg_data->c_str(), reg_data->size()));
132 
133   if (!data_sp->GetByteSize())
134     return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>(
135         LLVM_PRETTY_FUNCTION, "Failed to copy raw registers data.", error,
136         LLDBLog::Thread);
137 
138   std::shared_ptr<RegisterContextMemory> reg_ctx_memory =
139       std::make_shared<RegisterContextMemory>(
140           *this, 0, *GetDynamicRegisterInfo(), LLDB_INVALID_ADDRESS);
141   if (!reg_ctx_memory)
142     return ScriptedInterface::ErrorWithMessage<lldb::RegisterContextSP>(
143         LLVM_PRETTY_FUNCTION, "Failed to create a register context.", error,
144         LLDBLog::Thread);
145 
146   reg_ctx_memory->SetAllRegisterData(data_sp);
147   m_reg_context_sp = reg_ctx_memory;
148 
149   return m_reg_context_sp;
150 }
151 
152 bool ScriptedThread::LoadArtificialStackFrames() {
153   StructuredData::ArraySP arr_sp = GetInterface()->GetStackFrames();
154 
155   Status error;
156   if (!arr_sp)
157     return ScriptedInterface::ErrorWithMessage<bool>(
158         LLVM_PRETTY_FUNCTION, "Failed to get scripted thread stackframes.",
159         error, LLDBLog::Thread);
160 
161   size_t arr_size = arr_sp->GetSize();
162   if (arr_size > std::numeric_limits<uint32_t>::max())
163     return ScriptedInterface::ErrorWithMessage<bool>(
164         LLVM_PRETTY_FUNCTION,
165         llvm::Twine(
166             "StackFrame array size (" + llvm::Twine(arr_size) +
167             llvm::Twine(
168                 ") is greater than maximum authorized for a StackFrameList."))
169             .str(),
170         error, LLDBLog::Thread);
171 
172   StackFrameListSP frames = GetStackFrameList();
173 
174   for (size_t idx = 0; idx < arr_size; idx++) {
175     StructuredData::Dictionary *dict;
176     if (!arr_sp->GetItemAtIndexAsDictionary(idx, dict) || !dict)
177       return ScriptedInterface::ErrorWithMessage<bool>(
178           LLVM_PRETTY_FUNCTION,
179           llvm::Twine(
180               "Couldn't get artificial stackframe dictionary at index (" +
181               llvm::Twine(idx) + llvm::Twine(") from stackframe array."))
182               .str(),
183           error, LLDBLog::Thread);
184 
185     lldb::addr_t pc;
186     if (!dict->GetValueForKeyAsInteger("pc", pc))
187       return ScriptedInterface::ErrorWithMessage<bool>(
188           LLVM_PRETTY_FUNCTION,
189           "Couldn't find value for key 'pc' in stackframe dictionary.", error,
190           LLDBLog::Thread);
191 
192     Address symbol_addr;
193     symbol_addr.SetLoadAddress(pc, &this->GetProcess()->GetTarget());
194 
195     lldb::addr_t cfa = LLDB_INVALID_ADDRESS;
196     bool cfa_is_valid = false;
197     const bool behaves_like_zeroth_frame = false;
198     SymbolContext sc;
199     symbol_addr.CalculateSymbolContext(&sc);
200 
201     StackFrameSP synth_frame_sp = std::make_shared<StackFrame>(
202         this->shared_from_this(), idx, idx, cfa, cfa_is_valid, pc,
203         StackFrame::Kind::Artificial, behaves_like_zeroth_frame, &sc);
204 
205     if (!frames->SetFrameAtIndex(static_cast<uint32_t>(idx), synth_frame_sp))
206       return ScriptedInterface::ErrorWithMessage<bool>(
207           LLVM_PRETTY_FUNCTION,
208           llvm::Twine("Couldn't add frame (" + llvm::Twine(idx) +
209                       llvm::Twine(") to ScriptedThread StackFrameList."))
210               .str(),
211           error, LLDBLog::Thread);
212   }
213 
214   return true;
215 }
216 
217 bool ScriptedThread::CalculateStopInfo() {
218   StructuredData::DictionarySP dict_sp = GetInterface()->GetStopReason();
219 
220   Status error;
221   if (!dict_sp)
222     return ScriptedInterface::ErrorWithMessage<bool>(
223         LLVM_PRETTY_FUNCTION, "Failed to get scripted thread stop info.", error,
224         LLDBLog::Thread);
225 
226   lldb::StopInfoSP stop_info_sp;
227   lldb::StopReason stop_reason_type;
228 
229   if (!dict_sp->GetValueForKeyAsInteger("type", stop_reason_type))
230     return ScriptedInterface::ErrorWithMessage<bool>(
231         LLVM_PRETTY_FUNCTION,
232         "Couldn't find value for key 'type' in stop reason dictionary.", error,
233         LLDBLog::Thread);
234 
235   StructuredData::Dictionary *data_dict;
236   if (!dict_sp->GetValueForKeyAsDictionary("data", data_dict))
237     return ScriptedInterface::ErrorWithMessage<bool>(
238         LLVM_PRETTY_FUNCTION,
239         "Couldn't find value for key 'data' in stop reason dictionary.", error,
240         LLDBLog::Thread);
241 
242   switch (stop_reason_type) {
243   case lldb::eStopReasonNone:
244     return true;
245   case lldb::eStopReasonBreakpoint: {
246     lldb::break_id_t break_id;
247     data_dict->GetValueForKeyAsInteger("break_id", break_id,
248                                        LLDB_INVALID_BREAK_ID);
249     stop_info_sp =
250         StopInfo::CreateStopReasonWithBreakpointSiteID(*this, break_id);
251   } break;
252   case lldb::eStopReasonSignal: {
253     int signal;
254     llvm::StringRef description;
255     data_dict->GetValueForKeyAsInteger("signal", signal,
256                                        LLDB_INVALID_SIGNAL_NUMBER);
257     data_dict->GetValueForKeyAsString("desc", description);
258     stop_info_sp =
259         StopInfo::CreateStopReasonWithSignal(*this, signal, description.data());
260   } break;
261   case lldb::eStopReasonException: {
262 #if defined(__APPLE__)
263     StructuredData::Dictionary *mach_exception;
264     if (data_dict->GetValueForKeyAsDictionary("mach_exception",
265                                               mach_exception)) {
266       llvm::StringRef value;
267       mach_exception->GetValueForKeyAsString("type", value);
268       auto exc_type =
269           StopInfoMachException::MachException::ExceptionCode(value.data());
270 
271       if (!exc_type)
272         return false;
273 
274       uint32_t exc_data_size = 0;
275       llvm::SmallVector<uint64_t, 3> raw_codes;
276 
277       StructuredData::Array *exc_rawcodes;
278       mach_exception->GetValueForKeyAsArray("rawCodes", exc_rawcodes);
279       if (exc_rawcodes) {
280         auto fetch_data = [&raw_codes](StructuredData::Object *obj) {
281           if (!obj)
282             return false;
283           raw_codes.push_back(obj->GetIntegerValue());
284           return true;
285         };
286 
287         exc_rawcodes->ForEach(fetch_data);
288         exc_data_size = raw_codes.size();
289       }
290 
291       stop_info_sp = StopInfoMachException::CreateStopReasonWithMachException(
292           *this, *exc_type, exc_data_size,
293           exc_data_size >= 1 ? raw_codes[0] : 0,
294           exc_data_size >= 2 ? raw_codes[1] : 0,
295           exc_data_size >= 3 ? raw_codes[2] : 0);
296 
297       break;
298     }
299 #endif
300     stop_info_sp =
301         StopInfo::CreateStopReasonWithException(*this, "EXC_BAD_ACCESS");
302   } break;
303   default:
304     return ScriptedInterface::ErrorWithMessage<bool>(
305         LLVM_PRETTY_FUNCTION,
306         llvm::Twine("Unsupported stop reason type (" +
307                     llvm::Twine(stop_reason_type) + llvm::Twine(")."))
308             .str(),
309         error, LLDBLog::Thread);
310   }
311 
312   if (!stop_info_sp)
313     return false;
314 
315   SetStopInfo(stop_info_sp);
316   return true;
317 }
318 
319 void ScriptedThread::RefreshStateAfterStop() {
320   GetRegisterContext()->InvalidateIfNeeded(/*force=*/false);
321   LoadArtificialStackFrames();
322 }
323 
324 lldb::ScriptedThreadInterfaceSP ScriptedThread::GetInterface() const {
325   return m_scripted_thread_interface_sp;
326 }
327 
328 std::shared_ptr<DynamicRegisterInfo> ScriptedThread::GetDynamicRegisterInfo() {
329   CheckInterpreterAndScriptObject();
330 
331   if (!m_register_info_sp) {
332     StructuredData::DictionarySP reg_info = GetInterface()->GetRegisterInfo();
333 
334     Status error;
335     if (!reg_info)
336       return ScriptedInterface::ErrorWithMessage<
337           std::shared_ptr<DynamicRegisterInfo>>(
338           LLVM_PRETTY_FUNCTION, "Failed to get scripted thread registers info.",
339           error, LLDBLog::Thread);
340 
341     m_register_info_sp = std::make_shared<DynamicRegisterInfo>(
342         *reg_info, m_scripted_process.GetTarget().GetArchitecture());
343   }
344 
345   return m_register_info_sp;
346 }
347 
348 StructuredData::ObjectSP ScriptedThread::FetchThreadExtendedInfo() {
349   CheckInterpreterAndScriptObject();
350 
351   Status error;
352   StructuredData::ArraySP extended_info_sp = GetInterface()->GetExtendedInfo();
353 
354   if (!extended_info_sp || !extended_info_sp->GetSize())
355     return ScriptedInterface::ErrorWithMessage<StructuredData::ObjectSP>(
356         LLVM_PRETTY_FUNCTION, "No extended information found", error);
357 
358   return extended_info_sp;
359 }
360