1 | //===-- ScriptedThreadPlan.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 "lldb/Target/ThreadPlan.h" |
10 | |
11 | #include "lldb/Core/Debugger.h" |
12 | #include "lldb/Interpreter/CommandInterpreter.h" |
13 | #include "lldb/Interpreter/Interfaces/ScriptedThreadPlanInterface.h" |
14 | #include "lldb/Interpreter/ScriptInterpreter.h" |
15 | #include "lldb/Target/Process.h" |
16 | #include "lldb/Target/RegisterContext.h" |
17 | #include "lldb/Target/ScriptedThreadPlan.h" |
18 | #include "lldb/Target/Target.h" |
19 | #include "lldb/Target/Thread.h" |
20 | #include "lldb/Utility/LLDBLog.h" |
21 | #include "lldb/Utility/Log.h" |
22 | #include "lldb/Utility/State.h" |
23 | |
24 | using namespace lldb; |
25 | using namespace lldb_private; |
26 | |
27 | ScriptedThreadPlan::ScriptedThreadPlan(Thread &thread, const char *class_name, |
28 | const StructuredDataImpl &args_data) |
29 | : ThreadPlan(ThreadPlan::eKindPython, "Script based Thread Plan" , thread, |
30 | eVoteNoOpinion, eVoteNoOpinion), |
31 | m_class_name(class_name), m_args_data(args_data), m_did_push(false), |
32 | m_stop_others(false) { |
33 | ScriptInterpreter *interpreter = GetScriptInterpreter(); |
34 | if (!interpreter) { |
35 | SetPlanComplete(false); |
36 | // FIXME: error handling |
37 | // error = Status::FromErrorStringWithFormat( |
38 | // "ScriptedThreadPlan::%s () - ERROR: %s", __FUNCTION__, |
39 | // "Couldn't get script interpreter"); |
40 | return; |
41 | } |
42 | |
43 | m_interface = interpreter->CreateScriptedThreadPlanInterface(); |
44 | if (!m_interface) { |
45 | SetPlanComplete(false); |
46 | // FIXME: error handling |
47 | // error = Status::FromErrorStringWithFormat( |
48 | // "ScriptedThreadPlan::%s () - ERROR: %s", __FUNCTION__, |
49 | // "Script interpreter couldn't create Scripted Thread Plan Interface"); |
50 | return; |
51 | } |
52 | |
53 | SetIsControllingPlan(true); |
54 | SetOkayToDiscard(true); |
55 | SetPrivate(false); |
56 | } |
57 | |
58 | bool ScriptedThreadPlan::ValidatePlan(Stream *error) { |
59 | if (!m_did_push) |
60 | return true; |
61 | |
62 | if (!m_implementation_sp) { |
63 | if (error) |
64 | error->Printf(format: "Error constructing Python ThreadPlan: %s" , |
65 | m_error_str.empty() ? "<unknown error>" |
66 | : m_error_str.c_str()); |
67 | return false; |
68 | } |
69 | |
70 | return true; |
71 | } |
72 | |
73 | ScriptInterpreter *ScriptedThreadPlan::GetScriptInterpreter() { |
74 | return m_process.GetTarget().GetDebugger().GetScriptInterpreter(); |
75 | } |
76 | |
77 | void ScriptedThreadPlan::DidPush() { |
78 | // We set up the script side in DidPush, so that it can push other plans in |
79 | // the constructor, and doesn't have to care about the details of DidPush. |
80 | m_did_push = true; |
81 | if (m_interface) { |
82 | auto obj_or_err = m_interface->CreatePluginObject( |
83 | class_name: m_class_name, thread_plan_sp: this->shared_from_this(), args_sp: m_args_data); |
84 | if (!obj_or_err) { |
85 | m_error_str = llvm::toString(E: obj_or_err.takeError()); |
86 | SetPlanComplete(false); |
87 | } else |
88 | m_implementation_sp = *obj_or_err; |
89 | } |
90 | } |
91 | |
92 | bool ScriptedThreadPlan::ShouldStop(Event *event_ptr) { |
93 | Log *log = GetLog(mask: LLDBLog::Thread); |
94 | LLDB_LOGF(log, "%s called on Scripted Thread Plan: %s )" , |
95 | LLVM_PRETTY_FUNCTION, m_class_name.c_str()); |
96 | |
97 | bool should_stop = true; |
98 | if (m_implementation_sp) { |
99 | auto should_stop_or_err = m_interface->ShouldStop(event: event_ptr); |
100 | if (!should_stop_or_err) { |
101 | LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), should_stop_or_err.takeError(), |
102 | "Can't call ScriptedThreadPlan::ShouldStop." ); |
103 | SetPlanComplete(false); |
104 | } else |
105 | should_stop = *should_stop_or_err; |
106 | } |
107 | return should_stop; |
108 | } |
109 | |
110 | bool ScriptedThreadPlan::IsPlanStale() { |
111 | Log *log = GetLog(mask: LLDBLog::Thread); |
112 | LLDB_LOGF(log, "%s called on Scripted Thread Plan: %s )" , |
113 | LLVM_PRETTY_FUNCTION, m_class_name.c_str()); |
114 | |
115 | bool is_stale = true; |
116 | if (m_implementation_sp) { |
117 | auto is_stale_or_err = m_interface->IsStale(); |
118 | if (!is_stale_or_err) { |
119 | LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), is_stale_or_err.takeError(), |
120 | "Can't call ScriptedThreadPlan::IsStale." ); |
121 | SetPlanComplete(false); |
122 | } else |
123 | is_stale = *is_stale_or_err; |
124 | } |
125 | return is_stale; |
126 | } |
127 | |
128 | bool ScriptedThreadPlan::DoPlanExplainsStop(Event *event_ptr) { |
129 | Log *log = GetLog(mask: LLDBLog::Thread); |
130 | LLDB_LOGF(log, "%s called on Scripted Thread Plan: %s )" , |
131 | LLVM_PRETTY_FUNCTION, m_class_name.c_str()); |
132 | |
133 | bool explains_stop = true; |
134 | if (m_implementation_sp) { |
135 | auto explains_stop_or_error = m_interface->ExplainsStop(event: event_ptr); |
136 | if (!explains_stop_or_error) { |
137 | LLDB_LOG_ERROR(GetLog(LLDBLog::Thread), |
138 | explains_stop_or_error.takeError(), |
139 | "Can't call ScriptedThreadPlan::ExplainsStop." ); |
140 | SetPlanComplete(false); |
141 | } else |
142 | explains_stop = *explains_stop_or_error; |
143 | } |
144 | return explains_stop; |
145 | } |
146 | |
147 | bool ScriptedThreadPlan::MischiefManaged() { |
148 | Log *log = GetLog(mask: LLDBLog::Thread); |
149 | LLDB_LOGF(log, "%s called on Scripted Thread Plan: %s )" , |
150 | LLVM_PRETTY_FUNCTION, m_class_name.c_str()); |
151 | bool mischief_managed = true; |
152 | if (m_implementation_sp) { |
153 | // I don't really need mischief_managed, since it's simpler to just call |
154 | // SetPlanComplete in should_stop. |
155 | mischief_managed = IsPlanComplete(); |
156 | if (mischief_managed) { |
157 | // We need to cache the stop reason here we'll need it in GetDescription. |
158 | GetDescription(s: &m_stop_description, level: eDescriptionLevelBrief); |
159 | m_implementation_sp.reset(); |
160 | } |
161 | } |
162 | return mischief_managed; |
163 | } |
164 | |
165 | lldb::StateType ScriptedThreadPlan::GetPlanRunState() { |
166 | Log *log = GetLog(mask: LLDBLog::Thread); |
167 | LLDB_LOGF(log, "%s called on Scripted Thread Plan: %s )" , |
168 | LLVM_PRETTY_FUNCTION, m_class_name.c_str()); |
169 | lldb::StateType run_state = eStateRunning; |
170 | if (m_implementation_sp) |
171 | run_state = m_interface->GetRunState(); |
172 | return run_state; |
173 | } |
174 | |
175 | void ScriptedThreadPlan::GetDescription(Stream *s, |
176 | lldb::DescriptionLevel level) { |
177 | Log *log = GetLog(mask: LLDBLog::Thread); |
178 | LLDB_LOGF(log, "%s called on Scripted Thread Plan: %s )" , |
179 | LLVM_PRETTY_FUNCTION, m_class_name.c_str()); |
180 | if (m_implementation_sp) { |
181 | ScriptInterpreter *script_interp = GetScriptInterpreter(); |
182 | if (script_interp) { |
183 | lldb::StreamSP stream = std::make_shared<lldb_private::StreamString>(); |
184 | llvm::Error err = m_interface->GetStopDescription(stream); |
185 | if (err) { |
186 | LLDB_LOG_ERROR( |
187 | GetLog(LLDBLog::Thread), std::move(err), |
188 | "Can't call ScriptedThreadPlan::GetStopDescription: {0}" ); |
189 | s->Printf(format: "Scripted thread plan implemented by class %s." , |
190 | m_class_name.c_str()); |
191 | } else |
192 | s->PutCString( |
193 | cstr: reinterpret_cast<StreamString *>(stream.get())->GetData()); |
194 | } |
195 | return; |
196 | } |
197 | // It's an error not to have a description, so if we get here, we should |
198 | // add something. |
199 | if (m_stop_description.Empty()) |
200 | s->Printf(format: "Scripted thread plan implemented by class %s." , |
201 | m_class_name.c_str()); |
202 | s->PutCString(cstr: m_stop_description.GetData()); |
203 | } |
204 | |
205 | // The ones below are not currently exported to Python. |
206 | bool ScriptedThreadPlan::WillStop() { |
207 | Log *log = GetLog(mask: LLDBLog::Thread); |
208 | LLDB_LOGF(log, "%s called on Scripted Thread Plan: %s )" , |
209 | LLVM_PRETTY_FUNCTION, m_class_name.c_str()); |
210 | return true; |
211 | } |
212 | |
213 | bool ScriptedThreadPlan::DoWillResume(lldb::StateType resume_state, |
214 | bool current_plan) { |
215 | m_stop_description.Clear(); |
216 | return true; |
217 | } |
218 | |