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