forked from OSchip/llvm-project
[LLDB/Lua] add support for one-liner breakpoint callback
These callbacks are set using the following: breakpoint command add -s lua -o "print('hello world!')" The user supplied script is executed as: function (frame, bp_loc, ...) <body> end So the local variables 'frame', 'bp_loc' and vararg are all accessible. Any global variables declared will persist in the Lua interpreter. A user should never hold 'frame' and 'bp_loc' in a global variable as these userdatas are context dependent. Differential Revision: https://reviews.llvm.org/D91508
This commit is contained in:
parent
8e504615e9
commit
a0d7406ae8
|
@ -0,0 +1,15 @@
|
|||
template <typename SBClass>
|
||||
void
|
||||
PushSBClass (lua_State* L, SBClass* obj);
|
||||
|
||||
void
|
||||
PushSBClass (lua_State* L, lldb::SBFrame* frame_sb)
|
||||
{
|
||||
SWIG_NewPointerObj(L, frame_sb, SWIGTYPE_p_lldb__SBFrame, 0);
|
||||
}
|
||||
|
||||
void
|
||||
PushSBClass (lua_State* L, lldb::SBBreakpointLocation* breakpoint_location_sb)
|
||||
{
|
||||
SWIG_NewPointerObj(L, breakpoint_location_sb, SWIGTYPE_p_lldb__SBBreakpointLocation, 0);
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
%header %{
|
||||
|
||||
template <typename T>
|
||||
void
|
||||
PushSBClass(lua_State* L, T* obj);
|
||||
|
||||
%}
|
||||
|
||||
%wrapper %{
|
||||
|
||||
// This function is called from Lua::CallBreakpointCallback
|
||||
SWIGEXPORT llvm::Expected<bool>
|
||||
LLDBSwigLuaBreakpointCallbackFunction
|
||||
(
|
||||
lua_State *L,
|
||||
lldb::StackFrameSP stop_frame_sp,
|
||||
lldb::BreakpointLocationSP bp_loc_sp
|
||||
)
|
||||
{
|
||||
lldb::SBFrame sb_frame(stop_frame_sp);
|
||||
lldb::SBBreakpointLocation sb_bp_loc(bp_loc_sp);
|
||||
|
||||
// Push the Lua wrappers
|
||||
PushSBClass(L, &sb_frame);
|
||||
PushSBClass(L, &sb_bp_loc);
|
||||
|
||||
// Call into the Lua callback passing 'sb_frame' and 'sb_bp_loc'.
|
||||
// Expects a boolean return.
|
||||
if (lua_pcall(L, 2, 1, 0) != LUA_OK) {
|
||||
llvm::Error E = llvm::make_error<llvm::StringError>(
|
||||
llvm::formatv("{0}\n", lua_tostring(L, -1)),
|
||||
llvm::inconvertibleErrorCode());
|
||||
// Pop error message from the stack.
|
||||
lua_pop(L, 1);
|
||||
return std::move(E);
|
||||
}
|
||||
|
||||
// Boolean return from the callback
|
||||
bool stop = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
return stop;
|
||||
}
|
||||
|
||||
|
||||
%}
|
|
@ -14,8 +14,12 @@
|
|||
%include "headers.swig"
|
||||
|
||||
%{
|
||||
#include "llvm/Support/Error.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include "../bindings/lua/lua-swigsafecast.swig"
|
||||
using namespace lldb_private;
|
||||
using namespace lldb;
|
||||
%}
|
||||
|
||||
%include "interfaces.swig"
|
||||
%include "lua-wrapper.swig"
|
||||
|
|
|
@ -9,11 +9,34 @@
|
|||
#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);
|
||||
|
||||
#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");
|
||||
|
@ -57,6 +80,31 @@ 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}\n", 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) {
|
||||
lua_pushlightuserdata(m_lua_state, baton);
|
||||
lua_gettable(m_lua_state, LUA_REGISTRYINDEX);
|
||||
return LLDBSwigLuaBreakpointCallbackFunction(m_lua_state, stop_frame_sp,
|
||||
bp_loc_sp);
|
||||
}
|
||||
|
||||
llvm::Error Lua::LoadModule(llvm::StringRef filename) {
|
||||
FileSpec file(filename);
|
||||
if (!FileSystem::Instance().Exists(file)) {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#ifndef liblldb_Lua_h_
|
||||
#define liblldb_Lua_h_
|
||||
|
||||
#include "lldb/API/SBBreakpointLocation.h"
|
||||
#include "lldb/API/SBFrame.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/Support/Error.h"
|
||||
|
@ -29,6 +31,10 @@ public:
|
|||
~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);
|
||||
llvm::Error LoadModule(llvm::StringRef filename);
|
||||
llvm::Error ChangeIO(FILE *out, FILE *err);
|
||||
|
||||
|
|
|
@ -8,14 +8,17 @@
|
|||
|
||||
#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/Support/FormatAdapters.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
|
@ -174,6 +177,49 @@ 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();
|
||||
|
||||
llvm::Expected<bool> BoolOrErr =
|
||||
lua.CallBreakpointCallback(baton, stop_frame_sp, bp_loc_sp);
|
||||
if (llvm::Error E = BoolOrErr.takeError()) {
|
||||
debugger.GetErrorStream() << toString(std::move(E));
|
||||
return true;
|
||||
}
|
||||
|
||||
return *BoolOrErr;
|
||||
}
|
||||
|
||||
Status ScriptInterpreterLua::SetBreakpointCommandCallback(
|
||||
BreakpointOptions *bp_options, const char *command_body_text) {
|
||||
Status error;
|
||||
auto data_up = std::make_unique<CommandDataLua>();
|
||||
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);
|
||||
|
|
|
@ -10,11 +10,20 @@
|
|||
#define liblldb_ScriptInterpreterLua_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;
|
||||
}
|
||||
};
|
||||
|
||||
ScriptInterpreterLua(Debugger &debugger);
|
||||
|
||||
~ScriptInterpreterLua() override;
|
||||
|
@ -41,6 +50,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,6 +65,9 @@ public:
|
|||
llvm::Error EnterSession(lldb::user_id_t debugger_id);
|
||||
llvm::Error LeaveSession();
|
||||
|
||||
Status SetBreakpointCommandCallback(BreakpointOptions *bp_options,
|
||||
const char *command_body_text) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Lua> m_lua;
|
||||
bool m_session_is_active = false;
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# REQUIRES: lua
|
||||
# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t
|
||||
# RUN: %lldb -s %s --script-language lua %t 2>&1 | FileCheck %s
|
||||
b main
|
||||
breakpoint command add -s lua -o 'return false'
|
||||
run
|
||||
# CHECK: Process {{[0-9]+}} exited with status = 0
|
||||
breakpoint command add -s lua -o 'print(bacon)'
|
||||
run
|
||||
# CHECK: bacon
|
||||
# CHECK: Process {{[0-9]+}} exited with status = 0
|
||||
breakpoint command add -s lua -o "return true"
|
||||
run
|
||||
# CHECK: Process {{[0-9]+}} stopped
|
||||
breakpoint command add -s lua -o 'error("my error message")'
|
||||
run
|
||||
# CHECK: my error message
|
||||
# CHECK: Process {{[0-9]+}} stopped
|
|
@ -13,6 +13,30 @@ using namespace lldb_private;
|
|||
|
||||
extern "C" int luaopen_lldb(lua_State *L) { return 0; }
|
||||
|
||||
#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) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if _MSC_VER
|
||||
#pragma warning (pop)
|
||||
#endif
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
TEST(LuaTest, RunValid) {
|
||||
Lua lua;
|
||||
llvm::Error error = lua.Run("foo = 1");
|
||||
|
|
Loading…
Reference in New Issue