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