180814287SRaphael Isemann //===-- ScriptInterpreterLua.cpp ------------------------------------------===// 267de8962SJonas Devlieghere // 367de8962SJonas Devlieghere // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 467de8962SJonas Devlieghere // See https://llvm.org/LICENSE.txt for license information. 567de8962SJonas Devlieghere // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 667de8962SJonas Devlieghere // 767de8962SJonas Devlieghere //===----------------------------------------------------------------------===// 867de8962SJonas Devlieghere 967de8962SJonas Devlieghere #include "ScriptInterpreterLua.h" 1028613242SJonas Devlieghere #include "Lua.h" 11a0d7406aSPedro Tammela #include "lldb/Breakpoint/StoppointCallbackContext.h" 1267de8962SJonas Devlieghere #include "lldb/Core/Debugger.h" 1367de8962SJonas Devlieghere #include "lldb/Core/PluginManager.h" 1467de8962SJonas Devlieghere #include "lldb/Core/StreamFile.h" 1528613242SJonas Devlieghere #include "lldb/Interpreter/CommandReturnObject.h" 16a0d7406aSPedro Tammela #include "lldb/Target/ExecutionContext.h" 1767de8962SJonas Devlieghere #include "lldb/Utility/Stream.h" 1867de8962SJonas Devlieghere #include "lldb/Utility/StringList.h" 1928613242SJonas Devlieghere #include "lldb/Utility/Timer.h" 20*d853bd7aSPedro Tammela #include "llvm/ADT/StringRef.h" 21ed8184b7SJonas Devlieghere #include "llvm/Support/FormatAdapters.h" 22a0d7406aSPedro Tammela #include <memory> 23*d853bd7aSPedro Tammela #include <vector> 2467de8962SJonas Devlieghere 2567de8962SJonas Devlieghere using namespace lldb; 2667de8962SJonas Devlieghere using namespace lldb_private; 2767de8962SJonas Devlieghere 28bba9ba8dSJonas Devlieghere LLDB_PLUGIN_DEFINE(ScriptInterpreterLua) 29fbb4d1e4SJonas Devlieghere 30*d853bd7aSPedro Tammela enum ActiveIOHandler { 31*d853bd7aSPedro Tammela eIOHandlerNone, 32*d853bd7aSPedro Tammela eIOHandlerBreakpoint, 33*d853bd7aSPedro Tammela eIOHandlerWatchpoint 34*d853bd7aSPedro Tammela }; 35*d853bd7aSPedro Tammela 3628613242SJonas Devlieghere class IOHandlerLuaInterpreter : public IOHandlerDelegate, 3728613242SJonas Devlieghere public IOHandlerEditline { 3828613242SJonas Devlieghere public: 394164be72SJonas Devlieghere IOHandlerLuaInterpreter(Debugger &debugger, 40*d853bd7aSPedro Tammela ScriptInterpreterLua &script_interpreter, 41*d853bd7aSPedro Tammela ActiveIOHandler active_io_handler = eIOHandlerNone) 4228613242SJonas Devlieghere : IOHandlerEditline(debugger, IOHandler::Type::LuaInterpreter, "lua", 4328613242SJonas Devlieghere ">>> ", "..> ", true, debugger.GetUseColor(), 0, 4428613242SJonas Devlieghere *this, nullptr), 45*d853bd7aSPedro Tammela m_script_interpreter(script_interpreter), 46*d853bd7aSPedro Tammela m_active_io_handler(active_io_handler) { 47fa1b4a96SJonas Devlieghere llvm::cantFail(m_script_interpreter.GetLua().ChangeIO( 48fa1b4a96SJonas Devlieghere debugger.GetOutputFile().GetStream(), 49fa1b4a96SJonas Devlieghere debugger.GetErrorFile().GetStream())); 5045c971f7SJonas Devlieghere llvm::cantFail(m_script_interpreter.EnterSession(debugger.GetID())); 5145c971f7SJonas Devlieghere } 5245c971f7SJonas Devlieghere 535ddd4fc5SJonas Devlieghere ~IOHandlerLuaInterpreter() override { 5445c971f7SJonas Devlieghere llvm::cantFail(m_script_interpreter.LeaveSession()); 5545c971f7SJonas Devlieghere } 5628613242SJonas Devlieghere 57*d853bd7aSPedro Tammela void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { 58*d853bd7aSPedro Tammela const char *instructions = nullptr; 59*d853bd7aSPedro Tammela switch (m_active_io_handler) { 60*d853bd7aSPedro Tammela case eIOHandlerNone: 61*d853bd7aSPedro Tammela case eIOHandlerWatchpoint: 62*d853bd7aSPedro Tammela break; 63*d853bd7aSPedro Tammela case eIOHandlerBreakpoint: 64*d853bd7aSPedro Tammela instructions = "Enter your Lua command(s). Type 'quit' to end.\n" 65*d853bd7aSPedro Tammela "The commands are compiled as the body of the following " 66*d853bd7aSPedro Tammela "Lua function\n" 67*d853bd7aSPedro Tammela "function (frame, bp_loc, ...) end\n"; 68*d853bd7aSPedro Tammela SetPrompt(llvm::StringRef("..> ")); 69*d853bd7aSPedro Tammela break; 70*d853bd7aSPedro Tammela } 71*d853bd7aSPedro Tammela if (instructions == nullptr) 72*d853bd7aSPedro Tammela return; 73*d853bd7aSPedro Tammela if (interactive) 74*d853bd7aSPedro Tammela *io_handler.GetOutputStreamFileSP() << instructions; 75*d853bd7aSPedro Tammela } 76*d853bd7aSPedro Tammela 77*d853bd7aSPedro Tammela bool IOHandlerIsInputComplete(IOHandler &io_handler, 78*d853bd7aSPedro Tammela StringList &lines) override { 79*d853bd7aSPedro Tammela size_t last = lines.GetSize() - 1; 80*d853bd7aSPedro Tammela if (IsQuitCommand(lines.GetStringAtIndex(last))) { 81*d853bd7aSPedro Tammela if (m_active_io_handler == eIOHandlerBreakpoint) 82*d853bd7aSPedro Tammela lines.DeleteStringAtIndex(last); 83*d853bd7aSPedro Tammela return true; 84*d853bd7aSPedro Tammela } 85*d853bd7aSPedro Tammela StreamString str; 86*d853bd7aSPedro Tammela lines.Join("\n", str); 87*d853bd7aSPedro Tammela if (llvm::Error E = 88*d853bd7aSPedro Tammela m_script_interpreter.GetLua().CheckSyntax(str.GetString())) { 89*d853bd7aSPedro Tammela std::string error_str = toString(std::move(E)); 90*d853bd7aSPedro Tammela // Lua always errors out to incomplete code with '<eof>' 91*d853bd7aSPedro Tammela return error_str.find("<eof>") == std::string::npos; 92*d853bd7aSPedro Tammela } 93*d853bd7aSPedro Tammela // The breakpoint handler only exits with a explicit 'quit' 94*d853bd7aSPedro Tammela return m_active_io_handler != eIOHandlerBreakpoint; 95*d853bd7aSPedro Tammela } 96*d853bd7aSPedro Tammela 9728613242SJonas Devlieghere void IOHandlerInputComplete(IOHandler &io_handler, 9828613242SJonas Devlieghere std::string &data) override { 99*d853bd7aSPedro Tammela switch (m_active_io_handler) { 100*d853bd7aSPedro Tammela case eIOHandlerBreakpoint: { 101*d853bd7aSPedro Tammela auto *bp_options_vec = static_cast<std::vector<BreakpointOptions *> *>( 102*d853bd7aSPedro Tammela io_handler.GetUserData()); 103*d853bd7aSPedro Tammela for (auto *bp_options : *bp_options_vec) { 104*d853bd7aSPedro Tammela Status error = m_script_interpreter.SetBreakpointCommandCallback( 105*d853bd7aSPedro Tammela bp_options, data.c_str()); 106*d853bd7aSPedro Tammela if (error.Fail()) 107*d853bd7aSPedro Tammela *io_handler.GetErrorStreamFileSP() << error.AsCString() << '\n'; 108*d853bd7aSPedro Tammela } 109*d853bd7aSPedro Tammela io_handler.SetIsDone(true); 110*d853bd7aSPedro Tammela } break; 111*d853bd7aSPedro Tammela case eIOHandlerWatchpoint: 112*d853bd7aSPedro Tammela io_handler.SetIsDone(true); 113*d853bd7aSPedro Tammela break; 114*d853bd7aSPedro Tammela case eIOHandlerNone: 115*d853bd7aSPedro Tammela if (IsQuitCommand(data)) { 1161728dec2SJonas Devlieghere io_handler.SetIsDone(true); 1171728dec2SJonas Devlieghere return; 1181728dec2SJonas Devlieghere } 119*d853bd7aSPedro Tammela if (llvm::Error error = m_script_interpreter.GetLua().Run(data)) 120*d853bd7aSPedro Tammela *io_handler.GetErrorStreamFileSP() << toString(std::move(error)); 121*d853bd7aSPedro Tammela break; 12228613242SJonas Devlieghere } 12328613242SJonas Devlieghere } 12428613242SJonas Devlieghere 12528613242SJonas Devlieghere private: 1264164be72SJonas Devlieghere ScriptInterpreterLua &m_script_interpreter; 127*d853bd7aSPedro Tammela ActiveIOHandler m_active_io_handler; 128*d853bd7aSPedro Tammela 129*d853bd7aSPedro Tammela bool IsQuitCommand(llvm::StringRef cmd) { return cmd.rtrim() == "quit"; } 13028613242SJonas Devlieghere }; 13128613242SJonas Devlieghere 13267de8962SJonas Devlieghere ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger) 1334164be72SJonas Devlieghere : ScriptInterpreter(debugger, eScriptLanguageLua), 1344164be72SJonas Devlieghere m_lua(std::make_unique<Lua>()) {} 13567de8962SJonas Devlieghere 13667de8962SJonas Devlieghere ScriptInterpreterLua::~ScriptInterpreterLua() {} 13767de8962SJonas Devlieghere 13867de8962SJonas Devlieghere bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command, 13928613242SJonas Devlieghere CommandReturnObject *result, 14028613242SJonas Devlieghere const ExecuteScriptOptions &options) { 141ed8184b7SJonas Devlieghere if (command.empty()) { 142ed8184b7SJonas Devlieghere if (result) 143ed8184b7SJonas Devlieghere result->AppendError("empty command passed to lua\n"); 144ed8184b7SJonas Devlieghere return false; 145ed8184b7SJonas Devlieghere } 146ed8184b7SJonas Devlieghere 147ed8184b7SJonas Devlieghere llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> 148ed8184b7SJonas Devlieghere io_redirect_or_error = ScriptInterpreterIORedirect::Create( 149ed8184b7SJonas Devlieghere options.GetEnableIO(), m_debugger, result); 150ed8184b7SJonas Devlieghere if (!io_redirect_or_error) { 151ed8184b7SJonas Devlieghere if (result) 152ed8184b7SJonas Devlieghere result->AppendErrorWithFormatv( 153ed8184b7SJonas Devlieghere "failed to redirect I/O: {0}\n", 154ed8184b7SJonas Devlieghere llvm::fmt_consume(io_redirect_or_error.takeError())); 155ed8184b7SJonas Devlieghere else 156ed8184b7SJonas Devlieghere llvm::consumeError(io_redirect_or_error.takeError()); 157ed8184b7SJonas Devlieghere return false; 158ed8184b7SJonas Devlieghere } 159ed8184b7SJonas Devlieghere 160ed8184b7SJonas Devlieghere ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error; 161ed8184b7SJonas Devlieghere 162ed8184b7SJonas Devlieghere if (llvm::Error e = 163ed8184b7SJonas Devlieghere m_lua->ChangeIO(io_redirect.GetOutputFile()->GetStream(), 164ed8184b7SJonas Devlieghere io_redirect.GetErrorFile()->GetStream())) { 165ed8184b7SJonas Devlieghere result->AppendErrorWithFormatv("lua failed to redirect I/O: {0}\n", 166ed8184b7SJonas Devlieghere llvm::toString(std::move(e))); 167ed8184b7SJonas Devlieghere return false; 168ed8184b7SJonas Devlieghere } 169ed8184b7SJonas Devlieghere 1704164be72SJonas Devlieghere if (llvm::Error e = m_lua->Run(command)) { 17128613242SJonas Devlieghere result->AppendErrorWithFormatv( 17228613242SJonas Devlieghere "lua failed attempting to evaluate '{0}': {1}\n", command, 17328613242SJonas Devlieghere llvm::toString(std::move(e))); 17467de8962SJonas Devlieghere return false; 17567de8962SJonas Devlieghere } 176ed8184b7SJonas Devlieghere 177ed8184b7SJonas Devlieghere io_redirect.Flush(); 17828613242SJonas Devlieghere return true; 17928613242SJonas Devlieghere } 18067de8962SJonas Devlieghere 18167de8962SJonas Devlieghere void ScriptInterpreterLua::ExecuteInterpreterLoop() { 1825c1c8443SJonas Devlieghere LLDB_SCOPED_TIMER(); 18328613242SJonas Devlieghere 18428613242SJonas Devlieghere // At the moment, the only time the debugger does not have an input file 18528613242SJonas Devlieghere // handle is when this is called directly from lua, in which case it is 18628613242SJonas Devlieghere // both dangerous and unnecessary (not to mention confusing) to try to embed 18728613242SJonas Devlieghere // a running interpreter loop inside the already running lua interpreter 18828613242SJonas Devlieghere // loop, so we won't do it. 1896e3faaebSJonas Devlieghere if (!m_debugger.GetInputFile().IsValid()) 19028613242SJonas Devlieghere return; 19128613242SJonas Devlieghere 1926e3faaebSJonas Devlieghere IOHandlerSP io_handler_sp(new IOHandlerLuaInterpreter(m_debugger, *this)); 1936e3faaebSJonas Devlieghere m_debugger.RunIOHandlerAsync(io_handler_sp); 19467de8962SJonas Devlieghere } 19567de8962SJonas Devlieghere 196572b9f46SJonas Devlieghere bool ScriptInterpreterLua::LoadScriptingModule( 197572b9f46SJonas Devlieghere const char *filename, bool init_session, lldb_private::Status &error, 19800bb397bSJonas Devlieghere StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) { 199572b9f46SJonas Devlieghere 2001f80e515SJonas Devlieghere FileSystem::Instance().Collect(filename); 201572b9f46SJonas Devlieghere if (llvm::Error e = m_lua->LoadModule(filename)) { 202572b9f46SJonas Devlieghere error.SetErrorStringWithFormatv("lua failed to import '{0}': {1}\n", 203572b9f46SJonas Devlieghere filename, llvm::toString(std::move(e))); 204572b9f46SJonas Devlieghere return false; 205572b9f46SJonas Devlieghere } 206572b9f46SJonas Devlieghere return true; 207572b9f46SJonas Devlieghere } 208572b9f46SJonas Devlieghere 20967de8962SJonas Devlieghere void ScriptInterpreterLua::Initialize() { 21067de8962SJonas Devlieghere static llvm::once_flag g_once_flag; 21167de8962SJonas Devlieghere 21267de8962SJonas Devlieghere llvm::call_once(g_once_flag, []() { 21367de8962SJonas Devlieghere PluginManager::RegisterPlugin(GetPluginNameStatic(), 21467de8962SJonas Devlieghere GetPluginDescriptionStatic(), 21567de8962SJonas Devlieghere lldb::eScriptLanguageLua, CreateInstance); 21667de8962SJonas Devlieghere }); 21767de8962SJonas Devlieghere } 21867de8962SJonas Devlieghere 21967de8962SJonas Devlieghere void ScriptInterpreterLua::Terminate() {} 22067de8962SJonas Devlieghere 22145c971f7SJonas Devlieghere llvm::Error ScriptInterpreterLua::EnterSession(user_id_t debugger_id) { 22245c971f7SJonas Devlieghere if (m_session_is_active) 22345c971f7SJonas Devlieghere return llvm::Error::success(); 22445c971f7SJonas Devlieghere 22545c971f7SJonas Devlieghere const char *fmt_str = 22645c971f7SJonas Devlieghere "lldb.debugger = lldb.SBDebugger.FindDebuggerWithID({0}); " 22745c971f7SJonas Devlieghere "lldb.target = lldb.debugger:GetSelectedTarget(); " 22845c971f7SJonas Devlieghere "lldb.process = lldb.target:GetProcess(); " 22945c971f7SJonas Devlieghere "lldb.thread = lldb.process:GetSelectedThread(); " 23045c971f7SJonas Devlieghere "lldb.frame = lldb.thread:GetSelectedFrame()"; 23145c971f7SJonas Devlieghere return m_lua->Run(llvm::formatv(fmt_str, debugger_id).str()); 23245c971f7SJonas Devlieghere } 23345c971f7SJonas Devlieghere 23445c971f7SJonas Devlieghere llvm::Error ScriptInterpreterLua::LeaveSession() { 23545c971f7SJonas Devlieghere if (!m_session_is_active) 23645c971f7SJonas Devlieghere return llvm::Error::success(); 23745c971f7SJonas Devlieghere 23845c971f7SJonas Devlieghere m_session_is_active = false; 23945c971f7SJonas Devlieghere 24045c971f7SJonas Devlieghere llvm::StringRef str = "lldb.debugger = nil; " 24145c971f7SJonas Devlieghere "lldb.target = nil; " 24245c971f7SJonas Devlieghere "lldb.process = nil; " 24345c971f7SJonas Devlieghere "lldb.thread = nil; " 24445c971f7SJonas Devlieghere "lldb.frame = nil"; 24545c971f7SJonas Devlieghere return m_lua->Run(str); 24645c971f7SJonas Devlieghere } 24745c971f7SJonas Devlieghere 248a0d7406aSPedro Tammela bool ScriptInterpreterLua::BreakpointCallbackFunction( 249a0d7406aSPedro Tammela void *baton, StoppointCallbackContext *context, user_id_t break_id, 250a0d7406aSPedro Tammela user_id_t break_loc_id) { 251a0d7406aSPedro Tammela assert(context); 252a0d7406aSPedro Tammela 253a0d7406aSPedro Tammela ExecutionContext exe_ctx(context->exe_ctx_ref); 254a0d7406aSPedro Tammela Target *target = exe_ctx.GetTargetPtr(); 255a0d7406aSPedro Tammela if (target == nullptr) 256a0d7406aSPedro Tammela return true; 257a0d7406aSPedro Tammela 258a0d7406aSPedro Tammela StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP()); 259a0d7406aSPedro Tammela BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id); 260a0d7406aSPedro Tammela BreakpointLocationSP bp_loc_sp(breakpoint_sp->FindLocationByID(break_loc_id)); 261a0d7406aSPedro Tammela 262a0d7406aSPedro Tammela Debugger &debugger = target->GetDebugger(); 263a0d7406aSPedro Tammela ScriptInterpreterLua *lua_interpreter = static_cast<ScriptInterpreterLua *>( 264a0d7406aSPedro Tammela debugger.GetScriptInterpreter(true, eScriptLanguageLua)); 265a0d7406aSPedro Tammela Lua &lua = lua_interpreter->GetLua(); 266a0d7406aSPedro Tammela 267a0d7406aSPedro Tammela llvm::Expected<bool> BoolOrErr = 268a0d7406aSPedro Tammela lua.CallBreakpointCallback(baton, stop_frame_sp, bp_loc_sp); 269a0d7406aSPedro Tammela if (llvm::Error E = BoolOrErr.takeError()) { 270a0d7406aSPedro Tammela debugger.GetErrorStream() << toString(std::move(E)); 271a0d7406aSPedro Tammela return true; 272a0d7406aSPedro Tammela } 273a0d7406aSPedro Tammela 274a0d7406aSPedro Tammela return *BoolOrErr; 275a0d7406aSPedro Tammela } 276a0d7406aSPedro Tammela 277*d853bd7aSPedro Tammela void ScriptInterpreterLua::CollectDataForBreakpointCommandCallback( 278*d853bd7aSPedro Tammela std::vector<BreakpointOptions *> &bp_options_vec, 279*d853bd7aSPedro Tammela CommandReturnObject &result) { 280*d853bd7aSPedro Tammela IOHandlerSP io_handler_sp( 281*d853bd7aSPedro Tammela new IOHandlerLuaInterpreter(m_debugger, *this, eIOHandlerBreakpoint)); 282*d853bd7aSPedro Tammela io_handler_sp->SetUserData(&bp_options_vec); 283*d853bd7aSPedro Tammela m_debugger.RunIOHandlerAsync(io_handler_sp); 284*d853bd7aSPedro Tammela } 285*d853bd7aSPedro Tammela 286a0d7406aSPedro Tammela Status ScriptInterpreterLua::SetBreakpointCommandCallback( 287a0d7406aSPedro Tammela BreakpointOptions *bp_options, const char *command_body_text) { 288a0d7406aSPedro Tammela Status error; 289a0d7406aSPedro Tammela auto data_up = std::make_unique<CommandDataLua>(); 290a0d7406aSPedro Tammela error = m_lua->RegisterBreakpointCallback(data_up.get(), command_body_text); 291a0d7406aSPedro Tammela if (error.Fail()) 292a0d7406aSPedro Tammela return error; 293a0d7406aSPedro Tammela auto baton_sp = 294a0d7406aSPedro Tammela std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up)); 295a0d7406aSPedro Tammela bp_options->SetCallback(ScriptInterpreterLua::BreakpointCallbackFunction, 296a0d7406aSPedro Tammela baton_sp); 297a0d7406aSPedro Tammela return error; 298a0d7406aSPedro Tammela } 299a0d7406aSPedro Tammela 30067de8962SJonas Devlieghere lldb::ScriptInterpreterSP 30167de8962SJonas Devlieghere ScriptInterpreterLua::CreateInstance(Debugger &debugger) { 30267de8962SJonas Devlieghere return std::make_shared<ScriptInterpreterLua>(debugger); 30367de8962SJonas Devlieghere } 30467de8962SJonas Devlieghere 30567de8962SJonas Devlieghere lldb_private::ConstString ScriptInterpreterLua::GetPluginNameStatic() { 30667de8962SJonas Devlieghere static ConstString g_name("script-lua"); 30767de8962SJonas Devlieghere return g_name; 30867de8962SJonas Devlieghere } 30967de8962SJonas Devlieghere 31067de8962SJonas Devlieghere const char *ScriptInterpreterLua::GetPluginDescriptionStatic() { 31167de8962SJonas Devlieghere return "Lua script interpreter"; 31267de8962SJonas Devlieghere } 31367de8962SJonas Devlieghere 31467de8962SJonas Devlieghere lldb_private::ConstString ScriptInterpreterLua::GetPluginName() { 31567de8962SJonas Devlieghere return GetPluginNameStatic(); 31667de8962SJonas Devlieghere } 31767de8962SJonas Devlieghere 31867de8962SJonas Devlieghere uint32_t ScriptInterpreterLua::GetPluginVersion() { return 1; } 3194164be72SJonas Devlieghere 3204164be72SJonas Devlieghere Lua &ScriptInterpreterLua::GetLua() { return *m_lua; } 321