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