15ffd83dbSDimitry Andric //===-- ScriptInterpreterLua.cpp ------------------------------------------===// 2480093f4SDimitry Andric // 3480093f4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4480093f4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5480093f4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6480093f4SDimitry Andric // 7480093f4SDimitry Andric //===----------------------------------------------------------------------===// 8480093f4SDimitry Andric 9480093f4SDimitry Andric #include "ScriptInterpreterLua.h" 10480093f4SDimitry Andric #include "Lua.h" 11e8d8bef9SDimitry Andric #include "lldb/Breakpoint/StoppointCallbackContext.h" 12480093f4SDimitry Andric #include "lldb/Core/Debugger.h" 13480093f4SDimitry Andric #include "lldb/Core/PluginManager.h" 14480093f4SDimitry Andric #include "lldb/Core/StreamFile.h" 15480093f4SDimitry Andric #include "lldb/Interpreter/CommandReturnObject.h" 16e8d8bef9SDimitry Andric #include "lldb/Target/ExecutionContext.h" 17480093f4SDimitry Andric #include "lldb/Utility/Stream.h" 18480093f4SDimitry Andric #include "lldb/Utility/StringList.h" 19480093f4SDimitry Andric #include "lldb/Utility/Timer.h" 20e8d8bef9SDimitry Andric #include "llvm/ADT/StringRef.h" 215ffd83dbSDimitry Andric #include "llvm/Support/FormatAdapters.h" 22e8d8bef9SDimitry Andric #include <memory> 23e8d8bef9SDimitry Andric #include <vector> 24480093f4SDimitry Andric 25480093f4SDimitry Andric using namespace lldb; 26480093f4SDimitry Andric using namespace lldb_private; 27480093f4SDimitry Andric 285ffd83dbSDimitry Andric LLDB_PLUGIN_DEFINE(ScriptInterpreterLua) 295ffd83dbSDimitry Andric 30e8d8bef9SDimitry Andric enum ActiveIOHandler { 31e8d8bef9SDimitry Andric eIOHandlerNone, 32e8d8bef9SDimitry Andric eIOHandlerBreakpoint, 33e8d8bef9SDimitry Andric eIOHandlerWatchpoint 34e8d8bef9SDimitry Andric }; 35e8d8bef9SDimitry Andric 36480093f4SDimitry Andric class IOHandlerLuaInterpreter : public IOHandlerDelegate, 37480093f4SDimitry Andric public IOHandlerEditline { 38480093f4SDimitry Andric public: 39480093f4SDimitry Andric IOHandlerLuaInterpreter(Debugger &debugger, 40e8d8bef9SDimitry Andric ScriptInterpreterLua &script_interpreter, 41e8d8bef9SDimitry Andric ActiveIOHandler active_io_handler = eIOHandlerNone) 42480093f4SDimitry Andric : IOHandlerEditline(debugger, IOHandler::Type::LuaInterpreter, "lua", 43480093f4SDimitry Andric ">>> ", "..> ", true, debugger.GetUseColor(), 0, 44480093f4SDimitry Andric *this, nullptr), 45e8d8bef9SDimitry Andric m_script_interpreter(script_interpreter), 46e8d8bef9SDimitry Andric m_active_io_handler(active_io_handler) { 475ffd83dbSDimitry Andric llvm::cantFail(m_script_interpreter.GetLua().ChangeIO( 485ffd83dbSDimitry Andric debugger.GetOutputFile().GetStream(), 495ffd83dbSDimitry Andric debugger.GetErrorFile().GetStream())); 50480093f4SDimitry Andric llvm::cantFail(m_script_interpreter.EnterSession(debugger.GetID())); 51480093f4SDimitry Andric } 52480093f4SDimitry Andric 535ffd83dbSDimitry Andric ~IOHandlerLuaInterpreter() override { 54480093f4SDimitry Andric llvm::cantFail(m_script_interpreter.LeaveSession()); 55480093f4SDimitry Andric } 56480093f4SDimitry Andric 57e8d8bef9SDimitry Andric void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { 58e8d8bef9SDimitry Andric const char *instructions = nullptr; 59e8d8bef9SDimitry Andric switch (m_active_io_handler) { 60e8d8bef9SDimitry Andric case eIOHandlerNone: 61e8d8bef9SDimitry Andric case eIOHandlerWatchpoint: 62e8d8bef9SDimitry Andric break; 63e8d8bef9SDimitry Andric case eIOHandlerBreakpoint: 64e8d8bef9SDimitry Andric instructions = "Enter your Lua command(s). Type 'quit' to end.\n" 65e8d8bef9SDimitry Andric "The commands are compiled as the body of the following " 66e8d8bef9SDimitry Andric "Lua function\n" 67e8d8bef9SDimitry Andric "function (frame, bp_loc, ...) end\n"; 68e8d8bef9SDimitry Andric SetPrompt(llvm::StringRef("..> ")); 69e8d8bef9SDimitry Andric break; 70e8d8bef9SDimitry Andric } 71e8d8bef9SDimitry Andric if (instructions == nullptr) 72e8d8bef9SDimitry Andric return; 73e8d8bef9SDimitry Andric if (interactive) 74e8d8bef9SDimitry Andric *io_handler.GetOutputStreamFileSP() << instructions; 75e8d8bef9SDimitry Andric } 76e8d8bef9SDimitry Andric 77e8d8bef9SDimitry Andric bool IOHandlerIsInputComplete(IOHandler &io_handler, 78e8d8bef9SDimitry Andric StringList &lines) override { 79e8d8bef9SDimitry Andric size_t last = lines.GetSize() - 1; 80e8d8bef9SDimitry Andric if (IsQuitCommand(lines.GetStringAtIndex(last))) { 81e8d8bef9SDimitry Andric if (m_active_io_handler == eIOHandlerBreakpoint) 82e8d8bef9SDimitry Andric lines.DeleteStringAtIndex(last); 83e8d8bef9SDimitry Andric return true; 84e8d8bef9SDimitry Andric } 85e8d8bef9SDimitry Andric StreamString str; 86e8d8bef9SDimitry Andric lines.Join("\n", str); 87e8d8bef9SDimitry Andric if (llvm::Error E = 88e8d8bef9SDimitry Andric m_script_interpreter.GetLua().CheckSyntax(str.GetString())) { 89e8d8bef9SDimitry Andric std::string error_str = toString(std::move(E)); 90e8d8bef9SDimitry Andric // Lua always errors out to incomplete code with '<eof>' 91e8d8bef9SDimitry Andric return error_str.find("<eof>") == std::string::npos; 92e8d8bef9SDimitry Andric } 93e8d8bef9SDimitry Andric // The breakpoint handler only exits with a explicit 'quit' 94e8d8bef9SDimitry Andric return m_active_io_handler != eIOHandlerBreakpoint; 95e8d8bef9SDimitry Andric } 96e8d8bef9SDimitry Andric 97480093f4SDimitry Andric void IOHandlerInputComplete(IOHandler &io_handler, 98480093f4SDimitry Andric std::string &data) override { 99e8d8bef9SDimitry Andric switch (m_active_io_handler) { 100e8d8bef9SDimitry Andric case eIOHandlerBreakpoint: { 101e8d8bef9SDimitry Andric auto *bp_options_vec = static_cast<std::vector<BreakpointOptions *> *>( 102e8d8bef9SDimitry Andric io_handler.GetUserData()); 103e8d8bef9SDimitry Andric for (auto *bp_options : *bp_options_vec) { 104e8d8bef9SDimitry Andric Status error = m_script_interpreter.SetBreakpointCommandCallback( 105e8d8bef9SDimitry Andric bp_options, data.c_str()); 106e8d8bef9SDimitry Andric if (error.Fail()) 107e8d8bef9SDimitry Andric *io_handler.GetErrorStreamFileSP() << error.AsCString() << '\n'; 108e8d8bef9SDimitry Andric } 109e8d8bef9SDimitry Andric io_handler.SetIsDone(true); 110e8d8bef9SDimitry Andric } break; 111e8d8bef9SDimitry Andric case eIOHandlerWatchpoint: 112e8d8bef9SDimitry Andric io_handler.SetIsDone(true); 113e8d8bef9SDimitry Andric break; 114e8d8bef9SDimitry Andric case eIOHandlerNone: 115e8d8bef9SDimitry Andric if (IsQuitCommand(data)) { 1165ffd83dbSDimitry Andric io_handler.SetIsDone(true); 1175ffd83dbSDimitry Andric return; 1185ffd83dbSDimitry Andric } 119e8d8bef9SDimitry Andric if (llvm::Error error = m_script_interpreter.GetLua().Run(data)) 120e8d8bef9SDimitry Andric *io_handler.GetErrorStreamFileSP() << toString(std::move(error)); 121e8d8bef9SDimitry Andric break; 122480093f4SDimitry Andric } 123480093f4SDimitry Andric } 124480093f4SDimitry Andric 125480093f4SDimitry Andric private: 126480093f4SDimitry Andric ScriptInterpreterLua &m_script_interpreter; 127e8d8bef9SDimitry Andric ActiveIOHandler m_active_io_handler; 128e8d8bef9SDimitry Andric 129e8d8bef9SDimitry Andric bool IsQuitCommand(llvm::StringRef cmd) { return cmd.rtrim() == "quit"; } 130480093f4SDimitry Andric }; 131480093f4SDimitry Andric 132480093f4SDimitry Andric ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger) 133480093f4SDimitry Andric : ScriptInterpreter(debugger, eScriptLanguageLua), 134480093f4SDimitry Andric m_lua(std::make_unique<Lua>()) {} 135480093f4SDimitry Andric 136480093f4SDimitry Andric ScriptInterpreterLua::~ScriptInterpreterLua() {} 137480093f4SDimitry Andric 138480093f4SDimitry Andric bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command, 139480093f4SDimitry Andric CommandReturnObject *result, 140480093f4SDimitry Andric const ExecuteScriptOptions &options) { 1415ffd83dbSDimitry Andric if (command.empty()) { 1425ffd83dbSDimitry Andric if (result) 1435ffd83dbSDimitry Andric result->AppendError("empty command passed to lua\n"); 1445ffd83dbSDimitry Andric return false; 1455ffd83dbSDimitry Andric } 1465ffd83dbSDimitry Andric 1475ffd83dbSDimitry Andric llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>> 1485ffd83dbSDimitry Andric io_redirect_or_error = ScriptInterpreterIORedirect::Create( 1495ffd83dbSDimitry Andric options.GetEnableIO(), m_debugger, result); 1505ffd83dbSDimitry Andric if (!io_redirect_or_error) { 1515ffd83dbSDimitry Andric if (result) 1525ffd83dbSDimitry Andric result->AppendErrorWithFormatv( 1535ffd83dbSDimitry Andric "failed to redirect I/O: {0}\n", 1545ffd83dbSDimitry Andric llvm::fmt_consume(io_redirect_or_error.takeError())); 1555ffd83dbSDimitry Andric else 1565ffd83dbSDimitry Andric llvm::consumeError(io_redirect_or_error.takeError()); 1575ffd83dbSDimitry Andric return false; 1585ffd83dbSDimitry Andric } 1595ffd83dbSDimitry Andric 1605ffd83dbSDimitry Andric ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error; 1615ffd83dbSDimitry Andric 1625ffd83dbSDimitry Andric if (llvm::Error e = 1635ffd83dbSDimitry Andric m_lua->ChangeIO(io_redirect.GetOutputFile()->GetStream(), 1645ffd83dbSDimitry Andric io_redirect.GetErrorFile()->GetStream())) { 1655ffd83dbSDimitry Andric result->AppendErrorWithFormatv("lua failed to redirect I/O: {0}\n", 1665ffd83dbSDimitry Andric llvm::toString(std::move(e))); 1675ffd83dbSDimitry Andric return false; 1685ffd83dbSDimitry Andric } 1695ffd83dbSDimitry Andric 170480093f4SDimitry Andric if (llvm::Error e = m_lua->Run(command)) { 171480093f4SDimitry Andric result->AppendErrorWithFormatv( 172480093f4SDimitry Andric "lua failed attempting to evaluate '{0}': {1}\n", command, 173480093f4SDimitry Andric llvm::toString(std::move(e))); 174480093f4SDimitry Andric return false; 175480093f4SDimitry Andric } 1765ffd83dbSDimitry Andric 1775ffd83dbSDimitry Andric io_redirect.Flush(); 178480093f4SDimitry Andric return true; 179480093f4SDimitry Andric } 180480093f4SDimitry Andric 181480093f4SDimitry Andric void ScriptInterpreterLua::ExecuteInterpreterLoop() { 182e8d8bef9SDimitry Andric LLDB_SCOPED_TIMER(); 183480093f4SDimitry Andric 184480093f4SDimitry Andric // At the moment, the only time the debugger does not have an input file 185480093f4SDimitry Andric // handle is when this is called directly from lua, in which case it is 186480093f4SDimitry Andric // both dangerous and unnecessary (not to mention confusing) to try to embed 187480093f4SDimitry Andric // a running interpreter loop inside the already running lua interpreter 188480093f4SDimitry Andric // loop, so we won't do it. 1895ffd83dbSDimitry Andric if (!m_debugger.GetInputFile().IsValid()) 190480093f4SDimitry Andric return; 191480093f4SDimitry Andric 1925ffd83dbSDimitry Andric IOHandlerSP io_handler_sp(new IOHandlerLuaInterpreter(m_debugger, *this)); 1935ffd83dbSDimitry Andric m_debugger.RunIOHandlerAsync(io_handler_sp); 194480093f4SDimitry Andric } 195480093f4SDimitry Andric 196480093f4SDimitry Andric bool ScriptInterpreterLua::LoadScriptingModule( 197480093f4SDimitry Andric const char *filename, bool init_session, lldb_private::Status &error, 198e8d8bef9SDimitry Andric StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) { 199480093f4SDimitry Andric 2005ffd83dbSDimitry Andric FileSystem::Instance().Collect(filename); 201480093f4SDimitry Andric if (llvm::Error e = m_lua->LoadModule(filename)) { 202480093f4SDimitry Andric error.SetErrorStringWithFormatv("lua failed to import '{0}': {1}\n", 203480093f4SDimitry Andric filename, llvm::toString(std::move(e))); 204480093f4SDimitry Andric return false; 205480093f4SDimitry Andric } 206480093f4SDimitry Andric return true; 207480093f4SDimitry Andric } 208480093f4SDimitry Andric 209480093f4SDimitry Andric void ScriptInterpreterLua::Initialize() { 210480093f4SDimitry Andric static llvm::once_flag g_once_flag; 211480093f4SDimitry Andric 212480093f4SDimitry Andric llvm::call_once(g_once_flag, []() { 213480093f4SDimitry Andric PluginManager::RegisterPlugin(GetPluginNameStatic(), 214480093f4SDimitry Andric GetPluginDescriptionStatic(), 215480093f4SDimitry Andric lldb::eScriptLanguageLua, CreateInstance); 216480093f4SDimitry Andric }); 217480093f4SDimitry Andric } 218480093f4SDimitry Andric 219480093f4SDimitry Andric void ScriptInterpreterLua::Terminate() {} 220480093f4SDimitry Andric 221480093f4SDimitry Andric llvm::Error ScriptInterpreterLua::EnterSession(user_id_t debugger_id) { 222480093f4SDimitry Andric if (m_session_is_active) 223480093f4SDimitry Andric return llvm::Error::success(); 224480093f4SDimitry Andric 225480093f4SDimitry Andric const char *fmt_str = 226480093f4SDimitry Andric "lldb.debugger = lldb.SBDebugger.FindDebuggerWithID({0}); " 227480093f4SDimitry Andric "lldb.target = lldb.debugger:GetSelectedTarget(); " 228480093f4SDimitry Andric "lldb.process = lldb.target:GetProcess(); " 229480093f4SDimitry Andric "lldb.thread = lldb.process:GetSelectedThread(); " 230480093f4SDimitry Andric "lldb.frame = lldb.thread:GetSelectedFrame()"; 231480093f4SDimitry Andric return m_lua->Run(llvm::formatv(fmt_str, debugger_id).str()); 232480093f4SDimitry Andric } 233480093f4SDimitry Andric 234480093f4SDimitry Andric llvm::Error ScriptInterpreterLua::LeaveSession() { 235480093f4SDimitry Andric if (!m_session_is_active) 236480093f4SDimitry Andric return llvm::Error::success(); 237480093f4SDimitry Andric 238480093f4SDimitry Andric m_session_is_active = false; 239480093f4SDimitry Andric 240480093f4SDimitry Andric llvm::StringRef str = "lldb.debugger = nil; " 241480093f4SDimitry Andric "lldb.target = nil; " 242480093f4SDimitry Andric "lldb.process = nil; " 243480093f4SDimitry Andric "lldb.thread = nil; " 244480093f4SDimitry Andric "lldb.frame = nil"; 245480093f4SDimitry Andric return m_lua->Run(str); 246480093f4SDimitry Andric } 247480093f4SDimitry Andric 248e8d8bef9SDimitry Andric bool ScriptInterpreterLua::BreakpointCallbackFunction( 249e8d8bef9SDimitry Andric void *baton, StoppointCallbackContext *context, user_id_t break_id, 250e8d8bef9SDimitry Andric user_id_t break_loc_id) { 251e8d8bef9SDimitry Andric assert(context); 252e8d8bef9SDimitry Andric 253e8d8bef9SDimitry Andric ExecutionContext exe_ctx(context->exe_ctx_ref); 254e8d8bef9SDimitry Andric Target *target = exe_ctx.GetTargetPtr(); 255e8d8bef9SDimitry Andric if (target == nullptr) 256e8d8bef9SDimitry Andric return true; 257e8d8bef9SDimitry Andric 258e8d8bef9SDimitry Andric StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP()); 259e8d8bef9SDimitry Andric BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id); 260e8d8bef9SDimitry Andric BreakpointLocationSP bp_loc_sp(breakpoint_sp->FindLocationByID(break_loc_id)); 261e8d8bef9SDimitry Andric 262e8d8bef9SDimitry Andric Debugger &debugger = target->GetDebugger(); 263e8d8bef9SDimitry Andric ScriptInterpreterLua *lua_interpreter = static_cast<ScriptInterpreterLua *>( 264e8d8bef9SDimitry Andric debugger.GetScriptInterpreter(true, eScriptLanguageLua)); 265e8d8bef9SDimitry Andric Lua &lua = lua_interpreter->GetLua(); 266e8d8bef9SDimitry Andric 267e8d8bef9SDimitry Andric CommandDataLua *bp_option_data = static_cast<CommandDataLua *>(baton); 268e8d8bef9SDimitry Andric llvm::Expected<bool> BoolOrErr = lua.CallBreakpointCallback( 269e8d8bef9SDimitry Andric baton, stop_frame_sp, bp_loc_sp, bp_option_data->m_extra_args_sp); 270e8d8bef9SDimitry Andric if (llvm::Error E = BoolOrErr.takeError()) { 271e8d8bef9SDimitry Andric debugger.GetErrorStream() << toString(std::move(E)); 272e8d8bef9SDimitry Andric return true; 273e8d8bef9SDimitry Andric } 274e8d8bef9SDimitry Andric 275e8d8bef9SDimitry Andric return *BoolOrErr; 276e8d8bef9SDimitry Andric } 277e8d8bef9SDimitry Andric 278e8d8bef9SDimitry Andric void ScriptInterpreterLua::CollectDataForBreakpointCommandCallback( 279e8d8bef9SDimitry Andric std::vector<BreakpointOptions *> &bp_options_vec, 280e8d8bef9SDimitry Andric CommandReturnObject &result) { 281e8d8bef9SDimitry Andric IOHandlerSP io_handler_sp( 282e8d8bef9SDimitry Andric new IOHandlerLuaInterpreter(m_debugger, *this, eIOHandlerBreakpoint)); 283e8d8bef9SDimitry Andric io_handler_sp->SetUserData(&bp_options_vec); 284e8d8bef9SDimitry Andric m_debugger.RunIOHandlerAsync(io_handler_sp); 285e8d8bef9SDimitry Andric } 286e8d8bef9SDimitry Andric 287e8d8bef9SDimitry Andric Status ScriptInterpreterLua::SetBreakpointCommandCallbackFunction( 288e8d8bef9SDimitry Andric BreakpointOptions *bp_options, const char *function_name, 289e8d8bef9SDimitry Andric StructuredData::ObjectSP extra_args_sp) { 290e8d8bef9SDimitry Andric const char *fmt_str = "return {0}(frame, bp_loc, ...)"; 291e8d8bef9SDimitry Andric std::string oneliner = llvm::formatv(fmt_str, function_name).str(); 292e8d8bef9SDimitry Andric return RegisterBreakpointCallback(bp_options, oneliner.c_str(), 293e8d8bef9SDimitry Andric extra_args_sp); 294e8d8bef9SDimitry Andric } 295e8d8bef9SDimitry Andric 296e8d8bef9SDimitry Andric Status ScriptInterpreterLua::SetBreakpointCommandCallback( 297e8d8bef9SDimitry Andric BreakpointOptions *bp_options, const char *command_body_text) { 298e8d8bef9SDimitry Andric return RegisterBreakpointCallback(bp_options, command_body_text, {}); 299e8d8bef9SDimitry Andric } 300e8d8bef9SDimitry Andric 301e8d8bef9SDimitry Andric Status ScriptInterpreterLua::RegisterBreakpointCallback( 302e8d8bef9SDimitry Andric BreakpointOptions *bp_options, const char *command_body_text, 303e8d8bef9SDimitry Andric StructuredData::ObjectSP extra_args_sp) { 304e8d8bef9SDimitry Andric Status error; 305e8d8bef9SDimitry Andric auto data_up = std::make_unique<CommandDataLua>(extra_args_sp); 306e8d8bef9SDimitry Andric error = m_lua->RegisterBreakpointCallback(data_up.get(), command_body_text); 307e8d8bef9SDimitry Andric if (error.Fail()) 308e8d8bef9SDimitry Andric return error; 309e8d8bef9SDimitry Andric auto baton_sp = 310e8d8bef9SDimitry Andric std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up)); 311e8d8bef9SDimitry Andric bp_options->SetCallback(ScriptInterpreterLua::BreakpointCallbackFunction, 312e8d8bef9SDimitry Andric baton_sp); 313e8d8bef9SDimitry Andric return error; 314e8d8bef9SDimitry Andric } 315e8d8bef9SDimitry Andric 316480093f4SDimitry Andric lldb::ScriptInterpreterSP 317480093f4SDimitry Andric ScriptInterpreterLua::CreateInstance(Debugger &debugger) { 318480093f4SDimitry Andric return std::make_shared<ScriptInterpreterLua>(debugger); 319480093f4SDimitry Andric } 320480093f4SDimitry Andric 321480093f4SDimitry Andric lldb_private::ConstString ScriptInterpreterLua::GetPluginNameStatic() { 322480093f4SDimitry Andric static ConstString g_name("script-lua"); 323480093f4SDimitry Andric return g_name; 324480093f4SDimitry Andric } 325480093f4SDimitry Andric 326480093f4SDimitry Andric const char *ScriptInterpreterLua::GetPluginDescriptionStatic() { 327480093f4SDimitry Andric return "Lua script interpreter"; 328480093f4SDimitry Andric } 329480093f4SDimitry Andric 330480093f4SDimitry Andric lldb_private::ConstString ScriptInterpreterLua::GetPluginName() { 331480093f4SDimitry Andric return GetPluginNameStatic(); 332480093f4SDimitry Andric } 333480093f4SDimitry Andric 334480093f4SDimitry Andric uint32_t ScriptInterpreterLua::GetPluginVersion() { return 1; } 335480093f4SDimitry Andric 336480093f4SDimitry Andric Lua &ScriptInterpreterLua::GetLua() { return *m_lua; } 337