1//===-- ThreadPlanPython.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/ScriptInterpreter.h"
14#include "lldb/Target/Process.h"
15#include "lldb/Target/RegisterContext.h"
16#include "lldb/Target/Target.h"
17#include "lldb/Target/Thread.h"
18#include "lldb/Target/ThreadPlan.h"
19#include "lldb/Target/ThreadPlanPython.h"
20#include "lldb/Utility/Log.h"
21#include "lldb/Utility/State.h"
22
23using namespace lldb;
24using namespace lldb_private;
25
26// ThreadPlanPython
27
28ThreadPlanPython::ThreadPlanPython(Thread &thread, const char *class_name,
29                                   StructuredDataImpl *args_data)
30    : ThreadPlan(ThreadPlan::eKindPython, "Python based Thread Plan", thread,
31                 eVoteNoOpinion, eVoteNoOpinion),
32      m_class_name(class_name), m_args_data(args_data), m_did_push(false) {
33  SetIsMasterPlan(true);
34  SetOkayToDiscard(true);
35  SetPrivate(false);
36}
37
38ThreadPlanPython::~ThreadPlanPython() {
39  // FIXME, do I need to decrement the ref count on this implementation object
40  // to make it go away?
41}
42
43bool ThreadPlanPython::ValidatePlan(Stream *error) {
44  if (!m_did_push)
45    return true;
46
47  if (!m_implementation_sp) {
48    if (error)
49      error->Printf("Error constructing Python ThreadPlan: %s",
50          m_error_str.empty() ? "<unknown error>"
51                                : m_error_str.c_str());
52    return false;
53  }
54
55  return true;
56}
57
58ScriptInterpreter *ThreadPlanPython::GetScriptInterpreter() {
59  return m_process.GetTarget().GetDebugger().GetScriptInterpreter();
60}
61
62void ThreadPlanPython::DidPush() {
63  // We set up the script side in DidPush, so that it can push other plans in
64  // the constructor, and doesn't have to care about the details of DidPush.
65  m_did_push = true;
66  if (!m_class_name.empty()) {
67    ScriptInterpreter *script_interp = GetScriptInterpreter();
68    if (script_interp) {
69      m_implementation_sp = script_interp->CreateScriptedThreadPlan(
70          m_class_name.c_str(), m_args_data, m_error_str,
71          this->shared_from_this());
72    }
73  }
74}
75
76bool ThreadPlanPython::ShouldStop(Event *event_ptr) {
77  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
78  LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
79            m_class_name.c_str());
80
81  bool should_stop = true;
82  if (m_implementation_sp) {
83    ScriptInterpreter *script_interp = GetScriptInterpreter();
84    if (script_interp) {
85      bool script_error;
86      should_stop = script_interp->ScriptedThreadPlanShouldStop(
87          m_implementation_sp, event_ptr, script_error);
88      if (script_error)
89        SetPlanComplete(false);
90    }
91  }
92  return should_stop;
93}
94
95bool ThreadPlanPython::IsPlanStale() {
96  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
97  LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
98            m_class_name.c_str());
99
100  bool is_stale = true;
101  if (m_implementation_sp) {
102    ScriptInterpreter *script_interp = GetScriptInterpreter();
103    if (script_interp) {
104      bool script_error;
105      is_stale = script_interp->ScriptedThreadPlanIsStale(m_implementation_sp,
106                                                          script_error);
107      if (script_error)
108        SetPlanComplete(false);
109    }
110  }
111  return is_stale;
112}
113
114bool ThreadPlanPython::DoPlanExplainsStop(Event *event_ptr) {
115  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
116  LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
117            m_class_name.c_str());
118
119  bool explains_stop = true;
120  if (m_implementation_sp) {
121    ScriptInterpreter *script_interp = GetScriptInterpreter();
122    if (script_interp) {
123      bool script_error;
124      explains_stop = script_interp->ScriptedThreadPlanExplainsStop(
125          m_implementation_sp, event_ptr, script_error);
126      if (script_error)
127        SetPlanComplete(false);
128    }
129  }
130  return explains_stop;
131}
132
133bool ThreadPlanPython::MischiefManaged() {
134  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
135  LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
136            m_class_name.c_str());
137  bool mischief_managed = true;
138  if (m_implementation_sp) {
139    // I don't really need mischief_managed, since it's simpler to just call
140    // SetPlanComplete in should_stop.
141    mischief_managed = IsPlanComplete();
142    if (mischief_managed)
143      m_implementation_sp.reset();
144  }
145  return mischief_managed;
146}
147
148lldb::StateType ThreadPlanPython::GetPlanRunState() {
149  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
150  LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
151            m_class_name.c_str());
152  lldb::StateType run_state = eStateRunning;
153  if (m_implementation_sp) {
154    ScriptInterpreter *script_interp = GetScriptInterpreter();
155    if (script_interp) {
156      bool script_error;
157      run_state = script_interp->ScriptedThreadPlanGetRunState(
158          m_implementation_sp, script_error);
159    }
160  }
161  return run_state;
162}
163
164// The ones below are not currently exported to Python.
165
166bool ThreadPlanPython::StopOthers() {
167  // For now Python plans run all threads, but we should add some controls for
168  // this.
169  return false;
170}
171
172void ThreadPlanPython::GetDescription(Stream *s, lldb::DescriptionLevel level) {
173  s->Printf("Python thread plan implemented by class %s.",
174            m_class_name.c_str());
175}
176
177bool ThreadPlanPython::WillStop() {
178  Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_THREAD));
179  LLDB_LOGF(log, "%s called on Python Thread Plan: %s )", LLVM_PRETTY_FUNCTION,
180            m_class_name.c_str());
181  return true;
182}
183