Skip to content

Commit a0d7406

Browse files
committed
[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
1 parent 8e50461 commit a0d7406

File tree

9 files changed

+224
-0
lines changed

9 files changed

+224
-0
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
template <typename SBClass>
2+
void
3+
PushSBClass (lua_State* L, SBClass* obj);
4+
5+
void
6+
PushSBClass (lua_State* L, lldb::SBFrame* frame_sb)
7+
{
8+
SWIG_NewPointerObj(L, frame_sb, SWIGTYPE_p_lldb__SBFrame, 0);
9+
}
10+
11+
void
12+
PushSBClass (lua_State* L, lldb::SBBreakpointLocation* breakpoint_location_sb)
13+
{
14+
SWIG_NewPointerObj(L, breakpoint_location_sb, SWIGTYPE_p_lldb__SBBreakpointLocation, 0);
15+
}

lldb/bindings/lua/lua-wrapper.swig

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
%header %{
2+
3+
template <typename T>
4+
void
5+
PushSBClass(lua_State* L, T* obj);
6+
7+
%}
8+
9+
%wrapper %{
10+
11+
// This function is called from Lua::CallBreakpointCallback
12+
SWIGEXPORT llvm::Expected<bool>
13+
LLDBSwigLuaBreakpointCallbackFunction
14+
(
15+
lua_State *L,
16+
lldb::StackFrameSP stop_frame_sp,
17+
lldb::BreakpointLocationSP bp_loc_sp
18+
)
19+
{
20+
lldb::SBFrame sb_frame(stop_frame_sp);
21+
lldb::SBBreakpointLocation sb_bp_loc(bp_loc_sp);
22+
23+
// Push the Lua wrappers
24+
PushSBClass(L, &sb_frame);
25+
PushSBClass(L, &sb_bp_loc);
26+
27+
// Call into the Lua callback passing 'sb_frame' and 'sb_bp_loc'.
28+
// Expects a boolean return.
29+
if (lua_pcall(L, 2, 1, 0) != LUA_OK) {
30+
llvm::Error E = llvm::make_error<llvm::StringError>(
31+
llvm::formatv("{0}\n", lua_tostring(L, -1)),
32+
llvm::inconvertibleErrorCode());
33+
// Pop error message from the stack.
34+
lua_pop(L, 1);
35+
return std::move(E);
36+
}
37+
38+
// Boolean return from the callback
39+
bool stop = lua_toboolean(L, -1);
40+
lua_pop(L, 1);
41+
42+
return stop;
43+
}
44+
45+
46+
%}

lldb/bindings/lua/lua.swig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,12 @@
1414
%include "headers.swig"
1515

1616
%{
17+
#include "llvm/Support/Error.h"
18+
#include "llvm/Support/FormatVariadic.h"
19+
#include "../bindings/lua/lua-swigsafecast.swig"
1720
using namespace lldb_private;
1821
using namespace lldb;
1922
%}
2023

2124
%include "interfaces.swig"
25+
%include "lua-wrapper.swig"

lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,34 @@
99
#include "Lua.h"
1010
#include "lldb/Host/FileSystem.h"
1111
#include "lldb/Utility/FileSpec.h"
12+
#include "llvm/Support/Error.h"
1213
#include "llvm/Support/FormatVariadic.h"
1314

1415
using namespace lldb_private;
1516
using namespace lldb;
1617

18+
#pragma clang diagnostic push
19+
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
20+
21+
// Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has
22+
// C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is
23+
// incompatible with C
24+
#if _MSC_VER
25+
#pragma warning (push)
26+
#pragma warning (disable : 4190)
27+
#endif
28+
29+
extern "C" llvm::Expected<bool>
30+
LLDBSwigLuaBreakpointCallbackFunction(lua_State *L,
31+
lldb::StackFrameSP stop_frame_sp,
32+
lldb::BreakpointLocationSP bp_loc_sp);
33+
34+
#if _MSC_VER
35+
#pragma warning (pop)
36+
#endif
37+
38+
#pragma clang diagnostic pop
39+
1740
static int lldb_print(lua_State *L) {
1841
int n = lua_gettop(L);
1942
lua_getglobal(L, "io");
@@ -57,6 +80,31 @@ llvm::Error Lua::Run(llvm::StringRef buffer) {
5780
return e;
5881
}
5982

83+
llvm::Error Lua::RegisterBreakpointCallback(void *baton, const char *body) {
84+
lua_pushlightuserdata(m_lua_state, baton);
85+
const char *fmt_str = "return function(frame, bp_loc, ...) {0} end";
86+
std::string func_str = llvm::formatv(fmt_str, body).str();
87+
if (luaL_dostring(m_lua_state, func_str.c_str()) != LUA_OK) {
88+
llvm::Error e = llvm::make_error<llvm::StringError>(
89+
llvm::formatv("{0}\n", lua_tostring(m_lua_state, -1)),
90+
llvm::inconvertibleErrorCode());
91+
// Pop error message from the stack.
92+
lua_pop(m_lua_state, 2);
93+
return e;
94+
}
95+
lua_settable(m_lua_state, LUA_REGISTRYINDEX);
96+
return llvm::Error::success();
97+
}
98+
99+
llvm::Expected<bool>
100+
Lua::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
101+
lldb::BreakpointLocationSP bp_loc_sp) {
102+
lua_pushlightuserdata(m_lua_state, baton);
103+
lua_gettable(m_lua_state, LUA_REGISTRYINDEX);
104+
return LLDBSwigLuaBreakpointCallbackFunction(m_lua_state, stop_frame_sp,
105+
bp_loc_sp);
106+
}
107+
60108
llvm::Error Lua::LoadModule(llvm::StringRef filename) {
61109
FileSpec file(filename);
62110
if (!FileSystem::Instance().Exists(file)) {

lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#ifndef liblldb_Lua_h_
1010
#define liblldb_Lua_h_
1111

12+
#include "lldb/API/SBBreakpointLocation.h"
13+
#include "lldb/API/SBFrame.h"
1214
#include "lldb/lldb-types.h"
1315
#include "llvm/ADT/StringRef.h"
1416
#include "llvm/Support/Error.h"
@@ -29,6 +31,10 @@ class Lua {
2931
~Lua();
3032

3133
llvm::Error Run(llvm::StringRef buffer);
34+
llvm::Error RegisterBreakpointCallback(void *baton, const char *body);
35+
llvm::Expected<bool>
36+
CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
37+
lldb::BreakpointLocationSP bp_loc_sp);
3238
llvm::Error LoadModule(llvm::StringRef filename);
3339
llvm::Error ChangeIO(FILE *out, FILE *err);
3440

lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,17 @@
88

99
#include "ScriptInterpreterLua.h"
1010
#include "Lua.h"
11+
#include "lldb/Breakpoint/StoppointCallbackContext.h"
1112
#include "lldb/Core/Debugger.h"
1213
#include "lldb/Core/PluginManager.h"
1314
#include "lldb/Core/StreamFile.h"
1415
#include "lldb/Interpreter/CommandReturnObject.h"
16+
#include "lldb/Target/ExecutionContext.h"
1517
#include "lldb/Utility/Stream.h"
1618
#include "lldb/Utility/StringList.h"
1719
#include "lldb/Utility/Timer.h"
1820
#include "llvm/Support/FormatAdapters.h"
21+
#include <memory>
1922

2023
using namespace lldb;
2124
using namespace lldb_private;
@@ -174,6 +177,49 @@ llvm::Error ScriptInterpreterLua::LeaveSession() {
174177
return m_lua->Run(str);
175178
}
176179

180+
bool ScriptInterpreterLua::BreakpointCallbackFunction(
181+
void *baton, StoppointCallbackContext *context, user_id_t break_id,
182+
user_id_t break_loc_id) {
183+
assert(context);
184+
185+
ExecutionContext exe_ctx(context->exe_ctx_ref);
186+
Target *target = exe_ctx.GetTargetPtr();
187+
if (target == nullptr)
188+
return true;
189+
190+
StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
191+
BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id);
192+
BreakpointLocationSP bp_loc_sp(breakpoint_sp->FindLocationByID(break_loc_id));
193+
194+
Debugger &debugger = target->GetDebugger();
195+
ScriptInterpreterLua *lua_interpreter = static_cast<ScriptInterpreterLua *>(
196+
debugger.GetScriptInterpreter(true, eScriptLanguageLua));
197+
Lua &lua = lua_interpreter->GetLua();
198+
199+
llvm::Expected<bool> BoolOrErr =
200+
lua.CallBreakpointCallback(baton, stop_frame_sp, bp_loc_sp);
201+
if (llvm::Error E = BoolOrErr.takeError()) {
202+
debugger.GetErrorStream() << toString(std::move(E));
203+
return true;
204+
}
205+
206+
return *BoolOrErr;
207+
}
208+
209+
Status ScriptInterpreterLua::SetBreakpointCommandCallback(
210+
BreakpointOptions *bp_options, const char *command_body_text) {
211+
Status error;
212+
auto data_up = std::make_unique<CommandDataLua>();
213+
error = m_lua->RegisterBreakpointCallback(data_up.get(), command_body_text);
214+
if (error.Fail())
215+
return error;
216+
auto baton_sp =
217+
std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
218+
bp_options->SetCallback(ScriptInterpreterLua::BreakpointCallbackFunction,
219+
baton_sp);
220+
return error;
221+
}
222+
177223
lldb::ScriptInterpreterSP
178224
ScriptInterpreterLua::CreateInstance(Debugger &debugger) {
179225
return std::make_shared<ScriptInterpreterLua>(debugger);

lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,20 @@
1010
#define liblldb_ScriptInterpreterLua_h_
1111

1212
#include "lldb/Interpreter/ScriptInterpreter.h"
13+
#include "lldb/Utility/Status.h"
14+
#include "lldb/lldb-enumerations.h"
1315

1416
namespace lldb_private {
1517
class Lua;
1618
class ScriptInterpreterLua : public ScriptInterpreter {
1719
public:
20+
class CommandDataLua : public BreakpointOptions::CommandData {
21+
public:
22+
CommandDataLua() : BreakpointOptions::CommandData() {
23+
interpreter = lldb::eScriptLanguageLua;
24+
}
25+
};
26+
1827
ScriptInterpreterLua(Debugger &debugger);
1928

2029
~ScriptInterpreterLua() override;
@@ -41,6 +50,11 @@ class ScriptInterpreterLua : public ScriptInterpreter {
4150

4251
static const char *GetPluginDescriptionStatic();
4352

53+
static bool BreakpointCallbackFunction(void *baton,
54+
StoppointCallbackContext *context,
55+
lldb::user_id_t break_id,
56+
lldb::user_id_t break_loc_id);
57+
4458
// PluginInterface protocol
4559
lldb_private::ConstString GetPluginName() override;
4660

@@ -51,6 +65,9 @@ class ScriptInterpreterLua : public ScriptInterpreter {
5165
llvm::Error EnterSession(lldb::user_id_t debugger_id);
5266
llvm::Error LeaveSession();
5367

68+
Status SetBreakpointCommandCallback(BreakpointOptions *bp_options,
69+
const char *command_body_text) override;
70+
5471
private:
5572
std::unique_ptr<Lua> m_lua;
5673
bool m_session_is_active = false;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# REQUIRES: lua
2+
# RUN: echo "int main() { return 0; }" | %clang_host -x c - -o %t
3+
# RUN: %lldb -s %s --script-language lua %t 2>&1 | FileCheck %s
4+
b main
5+
breakpoint command add -s lua -o 'return false'
6+
run
7+
# CHECK: Process {{[0-9]+}} exited with status = 0
8+
breakpoint command add -s lua -o 'print(bacon)'
9+
run
10+
# CHECK: bacon
11+
# CHECK: Process {{[0-9]+}} exited with status = 0
12+
breakpoint command add -s lua -o "return true"
13+
run
14+
# CHECK: Process {{[0-9]+}} stopped
15+
breakpoint command add -s lua -o 'error("my error message")'
16+
run
17+
# CHECK: my error message
18+
# CHECK: Process {{[0-9]+}} stopped

lldb/unittests/ScriptInterpreter/Lua/LuaTests.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,30 @@ using namespace lldb_private;
1313

1414
extern "C" int luaopen_lldb(lua_State *L) { return 0; }
1515

16+
#pragma clang diagnostic push
17+
#pragma clang diagnostic ignored "-Wreturn-type-c-linkage"
18+
19+
// Disable warning C4190: 'LLDBSwigPythonBreakpointCallbackFunction' has
20+
// C-linkage specified, but returns UDT 'llvm::Expected<bool>' which is
21+
// incompatible with C
22+
#if _MSC_VER
23+
#pragma warning (push)
24+
#pragma warning (disable : 4190)
25+
#endif
26+
27+
extern "C" llvm::Expected<bool>
28+
LLDBSwigLuaBreakpointCallbackFunction(lua_State *L,
29+
lldb::StackFrameSP stop_frame_sp,
30+
lldb::BreakpointLocationSP bp_loc_sp) {
31+
return false;
32+
}
33+
34+
#if _MSC_VER
35+
#pragma warning (pop)
36+
#endif
37+
38+
#pragma clang diagnostic pop
39+
1640
TEST(LuaTest, RunValid) {
1741
Lua lua;
1842
llvm::Error error = lua.Run("foo = 1");

0 commit comments

Comments
 (0)