aboutsummaryrefslogtreecommitdiff
path: root/lldb/source/Plugins/ScriptInterpreter/Lua
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
commitb60736ec1405bb0a8dd40989f67ef4c93da068ab (patch)
tree5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /lldb/source/Plugins/ScriptInterpreter/Lua
parentcfca06d7963fa0909f90483b42a6d7d194d01e08 (diff)
Diffstat (limited to 'lldb/source/Plugins/ScriptInterpreter/Lua')
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp105
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h21
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp161
-rw-r--r--lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h43
4 files changed, 303 insertions, 27 deletions
diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp
index acd6128d84c5..f14e2732f6eb 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp
@@ -9,16 +9,66 @@
#include "Lua.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Utility/FileSpec.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
using namespace lldb_private;
using namespace lldb;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
+
+// Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has
+// C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is
+// incompatible with C
+#if _MSC_VER
+#pragma warning (push)
+#pragma warning (disable : 4190)
+#endif
+
+extern "C" llvm::Expected<bool> LLDBSwigLuaBreakpointCallbackFunction(
+ lua_State *L, lldb::StackFrameSP stop_frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl);
+
+#if _MSC_VER
+#pragma warning (pop)
+#endif
+
+#pragma clang diagnostic pop
+
+static int lldb_print(lua_State *L) {
+ int n = lua_gettop(L);
+ lua_getglobal(L, "io");
+ lua_getfield(L, -1, "stdout");
+ lua_getfield(L, -1, "write");
+ for (int i = 1; i <= n; i++) {
+ lua_pushvalue(L, -1); // write()
+ lua_pushvalue(L, -3); // io.stdout
+ luaL_tolstring(L, i, nullptr);
+ lua_pushstring(L, i != n ? "\t" : "\n");
+ lua_call(L, 3, 0);
+ }
+ return 0;
+}
+
+Lua::Lua() : m_lua_state(luaL_newstate()) {
+ assert(m_lua_state);
+ luaL_openlibs(m_lua_state);
+ luaopen_lldb(m_lua_state);
+ lua_pushcfunction(m_lua_state, lldb_print);
+ lua_setglobal(m_lua_state, "print");
+}
+
+Lua::~Lua() {
+ assert(m_lua_state);
+ lua_close(m_lua_state);
+}
+
llvm::Error Lua::Run(llvm::StringRef buffer) {
int error =
luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer") ||
lua_pcall(m_lua_state, 0, 0, 0);
- if (!error)
+ if (error == LUA_OK)
return llvm::Error::success();
llvm::Error e = llvm::make_error<llvm::StringError>(
@@ -29,6 +79,57 @@ llvm::Error Lua::Run(llvm::StringRef buffer) {
return e;
}
+llvm::Error Lua::RegisterBreakpointCallback(void *baton, const char *body) {
+ lua_pushlightuserdata(m_lua_state, baton);
+ const char *fmt_str = "return function(frame, bp_loc, ...) {0} end";
+ std::string func_str = llvm::formatv(fmt_str, body).str();
+ if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) {
+ llvm::Error e = llvm::make_error<llvm::StringError>(
+ llvm::formatv("{0}", lua_tostring(m_lua_state, -1)),
+ llvm::inconvertibleErrorCode());
+ // Pop error message from the stack.
+ lua_pop(m_lua_state, 2);
+ return e;
+ }
+ lua_settable(m_lua_state, LUA_REGISTRYINDEX);
+ return llvm::Error::success();
+}
+
+llvm::Expected<bool>
+Lua::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp,
+ StructuredData::ObjectSP extra_args_sp) {
+
+ lua_pushlightuserdata(m_lua_state, baton);
+ lua_gettable(m_lua_state, LUA_REGISTRYINDEX);
+ auto *extra_args_impl = [&]() -> StructuredDataImpl * {
+ if (extra_args_sp == nullptr)
+ return nullptr;
+ auto *extra_args_impl = new StructuredDataImpl();
+ extra_args_impl->SetObjectSP(extra_args_sp);
+ return extra_args_impl;
+ }();
+ return LLDBSwigLuaBreakpointCallbackFunction(m_lua_state, stop_frame_sp,
+ bp_loc_sp, extra_args_impl);
+}
+
+llvm::Error Lua::CheckSyntax(llvm::StringRef buffer) {
+ int error =
+ luaL_loadbuffer(m_lua_state, buffer.data(), buffer.size(), "buffer");
+ if (error == LUA_OK) {
+ // Pop buffer
+ lua_pop(m_lua_state, 1);
+ return llvm::Error::success();
+ }
+
+ llvm::Error e = llvm::make_error<llvm::StringError>(
+ llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)),
+ llvm::inconvertibleErrorCode());
+ // Pop error message from the stack.
+ lua_pop(m_lua_state, 1);
+ return e;
+}
+
llvm::Error Lua::LoadModule(llvm::StringRef filename) {
FileSpec file(filename);
if (!FileSystem::Instance().Exists(file)) {
@@ -44,7 +145,7 @@ llvm::Error Lua::LoadModule(llvm::StringRef filename) {
int error = luaL_loadfile(m_lua_state, filename.data()) ||
lua_pcall(m_lua_state, 0, 1, 0);
- if (error) {
+ if (error != LUA_OK) {
llvm::Error e = llvm::make_error<llvm::StringError>(
llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)),
llvm::inconvertibleErrorCode());
diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h
index 300115aac8a7..873440f0aab3 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h
@@ -9,6 +9,9 @@
#ifndef liblldb_Lua_h_
#define liblldb_Lua_h_
+#include "lldb/API/SBBreakpointLocation.h"
+#include "lldb/API/SBFrame.h"
+#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/lldb-types.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
@@ -25,19 +28,17 @@ int luaopen_lldb(lua_State *L);
class Lua {
public:
- Lua() : m_lua_state(luaL_newstate()) {
- assert(m_lua_state);
- luaL_openlibs(m_lua_state);
- luaopen_lldb(m_lua_state);
- }
-
- ~Lua() {
- assert(m_lua_state);
- luaL_openlibs(m_lua_state);
- }
+ Lua();
+ ~Lua();
llvm::Error Run(llvm::StringRef buffer);
+ llvm::Error RegisterBreakpointCallback(void *baton, const char *body);
+ llvm::Expected<bool>
+ CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
+ lldb::BreakpointLocationSP bp_loc_sp,
+ StructuredData::ObjectSP extra_args_sp);
llvm::Error LoadModule(llvm::StringRef filename);
+ llvm::Error CheckSyntax(llvm::StringRef buffer);
llvm::Error ChangeIO(FILE *out, FILE *err);
private:
diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
index 8cbeac4563c3..920e334f51aa 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
@@ -8,29 +8,42 @@
#include "ScriptInterpreterLua.h"
#include "Lua.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Target/ExecutionContext.h"
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/StringList.h"
#include "lldb/Utility/Timer.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FormatAdapters.h"
+#include <memory>
+#include <vector>
using namespace lldb;
using namespace lldb_private;
LLDB_PLUGIN_DEFINE(ScriptInterpreterLua)
+enum ActiveIOHandler {
+ eIOHandlerNone,
+ eIOHandlerBreakpoint,
+ eIOHandlerWatchpoint
+};
+
class IOHandlerLuaInterpreter : public IOHandlerDelegate,
public IOHandlerEditline {
public:
IOHandlerLuaInterpreter(Debugger &debugger,
- ScriptInterpreterLua &script_interpreter)
+ ScriptInterpreterLua &script_interpreter,
+ ActiveIOHandler active_io_handler = eIOHandlerNone)
: IOHandlerEditline(debugger, IOHandler::Type::LuaInterpreter, "lua",
">>> ", "..> ", true, debugger.GetUseColor(), 0,
*this, nullptr),
- m_script_interpreter(script_interpreter) {
+ m_script_interpreter(script_interpreter),
+ m_active_io_handler(active_io_handler) {
llvm::cantFail(m_script_interpreter.GetLua().ChangeIO(
debugger.GetOutputFile().GetStream(),
debugger.GetErrorFile().GetStream()));
@@ -41,20 +54,79 @@ public:
llvm::cantFail(m_script_interpreter.LeaveSession());
}
- void IOHandlerInputComplete(IOHandler &io_handler,
- std::string &data) override {
- if (llvm::StringRef(data).rtrim() == "quit") {
- io_handler.SetIsDone(true);
+ void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
+ const char *instructions = nullptr;
+ switch (m_active_io_handler) {
+ case eIOHandlerNone:
+ case eIOHandlerWatchpoint:
+ break;
+ case eIOHandlerBreakpoint:
+ instructions = "Enter your Lua command(s). Type 'quit' to end.\n"
+ "The commands are compiled as the body of the following "
+ "Lua function\n"
+ "function (frame, bp_loc, ...) end\n";
+ SetPrompt(llvm::StringRef("..> "));
+ break;
+ }
+ if (instructions == nullptr)
return;
+ if (interactive)
+ *io_handler.GetOutputStreamFileSP() << instructions;
+ }
+
+ bool IOHandlerIsInputComplete(IOHandler &io_handler,
+ StringList &lines) override {
+ size_t last = lines.GetSize() - 1;
+ if (IsQuitCommand(lines.GetStringAtIndex(last))) {
+ if (m_active_io_handler == eIOHandlerBreakpoint)
+ lines.DeleteStringAtIndex(last);
+ return true;
}
+ StreamString str;
+ lines.Join("\n", str);
+ if (llvm::Error E =
+ m_script_interpreter.GetLua().CheckSyntax(str.GetString())) {
+ std::string error_str = toString(std::move(E));
+ // Lua always errors out to incomplete code with '<eof>'
+ return error_str.find("<eof>") == std::string::npos;
+ }
+ // The breakpoint handler only exits with a explicit 'quit'
+ return m_active_io_handler != eIOHandlerBreakpoint;
+ }
- if (llvm::Error error = m_script_interpreter.GetLua().Run(data)) {
- *GetOutputStreamFileSP() << llvm::toString(std::move(error));
+ void IOHandlerInputComplete(IOHandler &io_handler,
+ std::string &data) override {
+ switch (m_active_io_handler) {
+ case eIOHandlerBreakpoint: {
+ auto *bp_options_vec = static_cast<std::vector<BreakpointOptions *> *>(
+ io_handler.GetUserData());
+ for (auto *bp_options : *bp_options_vec) {
+ Status error = m_script_interpreter.SetBreakpointCommandCallback(
+ bp_options, data.c_str());
+ if (error.Fail())
+ *io_handler.GetErrorStreamFileSP() << error.AsCString() << '\n';
+ }
+ io_handler.SetIsDone(true);
+ } break;
+ case eIOHandlerWatchpoint:
+ io_handler.SetIsDone(true);
+ break;
+ case eIOHandlerNone:
+ if (IsQuitCommand(data)) {
+ io_handler.SetIsDone(true);
+ return;
+ }
+ if (llvm::Error error = m_script_interpreter.GetLua().Run(data))
+ *io_handler.GetErrorStreamFileSP() << toString(std::move(error));
+ break;
}
}
private:
ScriptInterpreterLua &m_script_interpreter;
+ ActiveIOHandler m_active_io_handler;
+
+ bool IsQuitCommand(llvm::StringRef cmd) { return cmd.rtrim() == "quit"; }
};
ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger)
@@ -107,8 +179,7 @@ bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command,
}
void ScriptInterpreterLua::ExecuteInterpreterLoop() {
- static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
- Timer scoped_timer(func_cat, LLVM_PRETTY_FUNCTION);
+ LLDB_SCOPED_TIMER();
// At the moment, the only time the debugger does not have an input file
// handle is when this is called directly from lua, in which case it is
@@ -124,7 +195,7 @@ void ScriptInterpreterLua::ExecuteInterpreterLoop() {
bool ScriptInterpreterLua::LoadScriptingModule(
const char *filename, bool init_session, lldb_private::Status &error,
- StructuredData::ObjectSP *module_sp) {
+ StructuredData::ObjectSP *module_sp, FileSpec extra_search_dir) {
FileSystem::Instance().Collect(filename);
if (llvm::Error e = m_lua->LoadModule(filename)) {
@@ -174,6 +245,74 @@ llvm::Error ScriptInterpreterLua::LeaveSession() {
return m_lua->Run(str);
}
+bool ScriptInterpreterLua::BreakpointCallbackFunction(
+ void *baton, StoppointCallbackContext *context, user_id_t break_id,
+ user_id_t break_loc_id) {
+ assert(context);
+
+ ExecutionContext exe_ctx(context->exe_ctx_ref);
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target == nullptr)
+ return true;
+
+ StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
+ BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id);
+ BreakpointLocationSP bp_loc_sp(breakpoint_sp->FindLocationByID(break_loc_id));
+
+ Debugger &debugger = target->GetDebugger();
+ ScriptInterpreterLua *lua_interpreter = static_cast<ScriptInterpreterLua *>(
+ debugger.GetScriptInterpreter(true, eScriptLanguageLua));
+ Lua &lua = lua_interpreter->GetLua();
+
+ CommandDataLua *bp_option_data = static_cast<CommandDataLua *>(baton);
+ llvm::Expected<bool> BoolOrErr = lua.CallBreakpointCallback(
+ baton, stop_frame_sp, bp_loc_sp, bp_option_data->m_extra_args_sp);
+ if (llvm::Error E = BoolOrErr.takeError()) {
+ debugger.GetErrorStream() << toString(std::move(E));
+ return true;
+ }
+
+ return *BoolOrErr;
+}
+
+void ScriptInterpreterLua::CollectDataForBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ CommandReturnObject &result) {
+ IOHandlerSP io_handler_sp(
+ new IOHandlerLuaInterpreter(m_debugger, *this, eIOHandlerBreakpoint));
+ io_handler_sp->SetUserData(&bp_options_vec);
+ m_debugger.RunIOHandlerAsync(io_handler_sp);
+}
+
+Status ScriptInterpreterLua::SetBreakpointCommandCallbackFunction(
+ BreakpointOptions *bp_options, const char *function_name,
+ StructuredData::ObjectSP extra_args_sp) {
+ const char *fmt_str = "return {0}(frame, bp_loc, ...)";
+ std::string oneliner = llvm::formatv(fmt_str, function_name).str();
+ return RegisterBreakpointCallback(bp_options, oneliner.c_str(),
+ extra_args_sp);
+}
+
+Status ScriptInterpreterLua::SetBreakpointCommandCallback(
+ BreakpointOptions *bp_options, const char *command_body_text) {
+ return RegisterBreakpointCallback(bp_options, command_body_text, {});
+}
+
+Status ScriptInterpreterLua::RegisterBreakpointCallback(
+ BreakpointOptions *bp_options, const char *command_body_text,
+ StructuredData::ObjectSP extra_args_sp) {
+ Status error;
+ auto data_up = std::make_unique<CommandDataLua>(extra_args_sp);
+ error = m_lua->RegisterBreakpointCallback(data_up.get(), command_body_text);
+ if (error.Fail())
+ return error;
+ auto baton_sp =
+ std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
+ bp_options->SetCallback(ScriptInterpreterLua::BreakpointCallbackFunction,
+ baton_sp);
+ return error;
+}
+
lldb::ScriptInterpreterSP
ScriptInterpreterLua::CreateInstance(Debugger &debugger) {
return std::make_shared<ScriptInterpreterLua>(debugger);
diff --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
index bcc6ab24f6d0..1130ceee3c20 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
@@ -9,12 +9,27 @@
#ifndef liblldb_ScriptInterpreterLua_h_
#define liblldb_ScriptInterpreterLua_h_
+#include "lldb/Core/StructuredDataImpl.h"
#include "lldb/Interpreter/ScriptInterpreter.h"
+#include "lldb/Utility/Status.h"
+#include "lldb/lldb-enumerations.h"
namespace lldb_private {
class Lua;
class ScriptInterpreterLua : public ScriptInterpreter {
public:
+ class CommandDataLua : public BreakpointOptions::CommandData {
+ public:
+ CommandDataLua() : BreakpointOptions::CommandData() {
+ interpreter = lldb::eScriptLanguageLua;
+ }
+ CommandDataLua(StructuredData::ObjectSP extra_args_sp)
+ : BreakpointOptions::CommandData(), m_extra_args_sp(extra_args_sp) {
+ interpreter = lldb::eScriptLanguageLua;
+ }
+ StructuredData::ObjectSP m_extra_args_sp;
+ };
+
ScriptInterpreterLua(Debugger &debugger);
~ScriptInterpreterLua() override;
@@ -25,10 +40,10 @@ public:
void ExecuteInterpreterLoop() override;
- bool
- LoadScriptingModule(const char *filename, bool init_session,
- lldb_private::Status &error,
- StructuredData::ObjectSP *module_sp = nullptr) override;
+ bool LoadScriptingModule(const char *filename, bool init_session,
+ lldb_private::Status &error,
+ StructuredData::ObjectSP *module_sp = nullptr,
+ FileSpec extra_search_dir = {}) override;
// Static Functions
static void Initialize();
@@ -41,6 +56,11 @@ public:
static const char *GetPluginDescriptionStatic();
+ static bool BreakpointCallbackFunction(void *baton,
+ StoppointCallbackContext *context,
+ lldb::user_id_t break_id,
+ lldb::user_id_t break_loc_id);
+
// PluginInterface protocol
lldb_private::ConstString GetPluginName() override;
@@ -51,9 +71,24 @@ public:
llvm::Error EnterSession(lldb::user_id_t debugger_id);
llvm::Error LeaveSession();
+ void CollectDataForBreakpointCommandCallback(
+ std::vector<BreakpointOptions *> &bp_options_vec,
+ CommandReturnObject &result) override;
+
+ Status SetBreakpointCommandCallback(BreakpointOptions *bp_options,
+ const char *command_body_text) override;
+
+ Status SetBreakpointCommandCallbackFunction(
+ BreakpointOptions *bp_options, const char *function_name,
+ StructuredData::ObjectSP extra_args_sp) override;
+
private:
std::unique_ptr<Lua> m_lua;
bool m_session_is_active = false;
+
+ Status RegisterBreakpointCallback(BreakpointOptions *bp_options,
+ const char *command_body_text,
+ StructuredData::ObjectSP extra_args_sp);
};
} // namespace lldb_private