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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of lldb/source/Plugins/Process/scripted/ScriptedThread.cpp