[lldb/Lua] add support for Lua function breakpoint
authorPedro Tammela <pctammela@gmail.com>
Mon, 21 Dec 2020 17:59:35 +0000 (17:59 +0000)
committerPedro Tammela <pctammela@gmail.com>
Mon, 25 Jan 2021 23:40:57 +0000 (23:40 +0000)
Adds support for running a Lua function when a breakpoint is hit.

Example:
   breakpoint command add -s lua -F abc

The above runs the Lua function 'abc' passing 2 arguments. 'frame', 'bp_loc' and 'extra_args'.

A third parameter 'extra_args' is only present when there is structured data
declared in the command line.

Example:
   breakpoint command add -s lua -F abc -k foo -v bar

Differential Revision: https://reviews.llvm.org/D93649

lldb/bindings/lua/lua-swigsafecast.swig
lldb/bindings/lua/lua-typemaps.swig
lldb/bindings/lua/lua-wrapper.swig
lldb/source/Plugins/ScriptInterpreter/Lua/Lua.cpp
lldb/source/Plugins/ScriptInterpreter/Lua/Lua.h
lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
lldb/test/Shell/ScriptInterpreter/Lua/breakpoint_function_callback.test [new file with mode: 0644]
lldb/unittests/ScriptInterpreter/Lua/LuaTests.cpp

index d193089..a3ed372 100644 (file)
@@ -13,3 +13,9 @@ PushSBClass (lua_State* L, lldb::SBBreakpointLocation* breakpoint_location_sb)
 {
    SWIG_NewPointerObj(L, breakpoint_location_sb, SWIGTYPE_p_lldb__SBBreakpointLocation, 0);
 }
+
+void
+PushSBClass (lua_State* L, lldb::SBStructuredData* structured_data_sb)
+{
+   SWIG_NewPointerObj(L, structured_data_sb, SWIGTYPE_p_lldb__SBStructuredData, 0);
+}
index 32435b9..d912137 100644 (file)
@@ -62,6 +62,15 @@ LLDB_NUMBER_TYPEMAP(signed long long);
 
 //===----------------------------------------------------------------------===//
 
+// FIXME:
+//  Ideally all the typemaps should be revisited in a future SB API revision.
+//  Typemaps, usually, modifies the function signatures and might spawn
+//  different LLDB APIs across languages (C++, Python, Lua...).
+//  Historically, typemaps have been used to replace SWIG's deficiencies,
+//  but SWIG itself evolved and some API design choices are now redundant.
+
+//===----------------------------------------------------------------------===//
+
 // Typemap definitions to allow SWIG to properly handle char buffer.
 
 // typemap for a char buffer
index 4e3df74..90c2292 100644 (file)
@@ -14,19 +14,30 @@ LLDBSwigLuaBreakpointCallbackFunction
 (
    lua_State *L,
    lldb::StackFrameSP stop_frame_sp,
-   lldb::BreakpointLocationSP bp_loc_sp
+   lldb::BreakpointLocationSP bp_loc_sp,
+   StructuredDataImpl *extra_args_impl
 )
 {
    lldb::SBFrame sb_frame(stop_frame_sp);
    lldb::SBBreakpointLocation sb_bp_loc(bp_loc_sp);
+   int nargs = 2;
+
+   llvm::Optional<lldb::SBStructuredData> extra_args;
+   if (extra_args_impl)
+      extra_args = lldb::SBStructuredData(extra_args_impl);
 
    // Push the Lua wrappers
    PushSBClass(L, &sb_frame);
    PushSBClass(L, &sb_bp_loc);
 
+   if (extra_args.hasValue()) {
+      PushSBClass(L, extra_args.getPointer());
+      nargs++;
+   }
+
    // 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) {
+   if (lua_pcall(L, nargs, 1, 0) != LUA_OK) {
       llvm::Error E = llvm::make_error<llvm::StringError>(
             llvm::formatv("{0}\n", lua_tostring(L, -1)),
             llvm::inconvertibleErrorCode());
index ec946d1..f14e273 100644 (file)
@@ -26,10 +26,9 @@ using namespace lldb;
 #pragma warning (disable : 4190)
 #endif
 
-extern "C" llvm::Expected<bool>
-LLDBSwigLuaBreakpointCallbackFunction(lua_State *L,
-                                      lldb::StackFrameSP stop_frame_sp,
-                                      lldb::BreakpointLocationSP bp_loc_sp);
+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)
@@ -98,11 +97,20 @@ llvm::Error Lua::RegisterBreakpointCallback(void *baton, const char *body) {
 
 llvm::Expected<bool>
 Lua::CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
-                            lldb::BreakpointLocationSP bp_loc_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);
+                                               bp_loc_sp, extra_args_impl);
 }
 
 llvm::Error Lua::CheckSyntax(llvm::StringRef buffer) {
index 84cc014..873440f 100644 (file)
@@ -11,6 +11,7 @@
 
 #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"
@@ -34,7 +35,8 @@ public:
   llvm::Error RegisterBreakpointCallback(void *baton, const char *body);
   llvm::Expected<bool>
   CallBreakpointCallback(void *baton, lldb::StackFrameSP stop_frame_sp,
-                         lldb::BreakpointLocationSP bp_loc_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);
index 1dbadb9..920e334 100644 (file)
@@ -264,8 +264,9 @@ bool ScriptInterpreterLua::BreakpointCallbackFunction(
       debugger.GetScriptInterpreter(true, eScriptLanguageLua));
   Lua &lua = lua_interpreter->GetLua();
 
-  llvm::Expected<bool> BoolOrErr =
-      lua.CallBreakpointCallback(baton, stop_frame_sp, bp_loc_sp);
+  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;
@@ -283,10 +284,25 @@ void ScriptInterpreterLua::CollectDataForBreakpointCommandCallback(
   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>();
+  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;
index 6c3fc01..1130cee 100644 (file)
@@ -9,6 +9,7 @@
 #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"
@@ -22,6 +23,11 @@ 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);
@@ -72,9 +78,17 @@ public:
   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
diff --git a/lldb/test/Shell/ScriptInterpreter/Lua/breakpoint_function_callback.test b/lldb/test/Shell/ScriptInterpreter/Lua/breakpoint_function_callback.test
new file mode 100644 (file)
index 0000000..5df8dd3
--- /dev/null
@@ -0,0 +1,23 @@
+# 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
+script
+function abc(a, b, c, ...)
+print(c)
+if c then print(c:GetValueForKey("foo"):GetStringValue(32)) end
+end
+quit
+breakpoint command add -s lua -F abc
+r
+# CHECK: nil
+breakpoint command add -s lua -F abc -k foo -v 123pizza!
+r
+# CHECK: <userdata of type 'lldb::SBStructuredData *' at {{0x[[:xdigit:]]+}}>
+# CHECK: 123pizza!
+breakpoint command add -s lua -o "abc(frame, bp_loc, ...)"
+r
+# CHECK: nil
+breakpoint command add -s lua -F typo
+r
+# CHECK: attempt to call a nil value
index 74578af..76ce3bc 100644 (file)
@@ -24,10 +24,9 @@ extern "C" int luaopen_lldb(lua_State *L) { return 0; }
 #pragma warning (disable : 4190)
 #endif
 
-extern "C" llvm::Expected<bool>
-LLDBSwigLuaBreakpointCallbackFunction(lua_State *L,
-                                      lldb::StackFrameSP stop_frame_sp,
-                                      lldb::BreakpointLocationSP bp_loc_sp) {
+extern "C" llvm::Expected<bool> LLDBSwigLuaBreakpointCallbackFunction(
+    lua_State *L, lldb::StackFrameSP stop_frame_sp,
+    lldb::BreakpointLocationSP bp_loc_sp, StructuredDataImpl *extra_args_impl) {
   return false;
 }