diff --git a/lldb/bindings/interface/SBBreakpoint.i b/lldb/bindings/interface/SBBreakpoint.i --- a/lldb/bindings/interface/SBBreakpoint.i +++ b/lldb/bindings/interface/SBBreakpoint.i @@ -177,6 +177,11 @@ const char * GetQueueName () const; + %feature("docstring", " + Set a specific closure to be called when the breakpoint is hit.") SetScriptCallback; + void + SetScriptCallback (const SBBreakpointCallbackBatonSP &baton_sp); + %feature("docstring", " Set the name of the script function to be called when the breakpoint is hit.") SetScriptCallbackFunction; void diff --git a/lldb/bindings/lua/lua-typemaps.swig b/lldb/bindings/lua/lua-typemaps.swig --- a/lldb/bindings/lua/lua-typemaps.swig +++ b/lldb/bindings/lua/lua-typemaps.swig @@ -314,3 +314,29 @@ } //===----------------------------------------------------------------------===// + +// Typemap for closure callback in external scripting environment + +%typemap(arginit) const SBBreakpointCallbackBatonSP &baton_sp { + // We could `SBBreakpointCallbackBatonSP baton_sp;` inside "%typemap(in)" + // but SWIG puts "%typemap(in)" part and the real call in 2 *different* scopes + // so the smart pointer destructs earlier than it is used by the call. + $1 = new SBBreakpointCallbackBatonSP(); +} + +%typemap(in) const SBBreakpointCallbackBatonSP &baton_sp { + if (lua_isfunction(L, $input)) { + auto data_up = std::make_unique(L); + lua_pushlightuserdata(L, data_up.get()); + lua_pushvalue(L, $input); + lua_settable(L, LUA_REGISTRYINDEX); + *$1 = std::make_shared(std::move(data_up)); + } + else { + luaL_error(L, "Expect a Lua function."); + } +} + +%typemap(freearg) const SBBreakpointCallbackBatonSP &baton_sp { + free($1); +} diff --git a/lldb/bindings/lua/lua-wrapper.swig b/lldb/bindings/lua/lua-wrapper.swig --- a/lldb/bindings/lua/lua-wrapper.swig +++ b/lldb/bindings/lua/lua-wrapper.swig @@ -98,4 +98,52 @@ return luaL_error(L, "You cannot close a file handle used by lldb."); } +// These are external callback wrappers. + +bool SBBreakpointHitCallbackLua( + void *baton, + SBFrame &sb_frame, + SBBreakpointLocation &sb_bp_loc +); + +struct CallbackDataLua : public CallbackData { +public: + CallbackDataLua(lua_State *l) : L(l) { + callback = SBBreakpointHitCallbackLua; + callback_baton = (void *) this; + } + lua_State *L; +}; + + +bool SBBreakpointHitCallbackLua( + void *baton, + SBFrame &sb_frame, + SBBreakpointLocation &sb_bp_loc +) +{ + auto data = (CallbackDataLua *) baton; + + lua_State *L = data->L; + lua_pushlightuserdata(L, data); + lua_gettable(L, LUA_REGISTRYINDEX); + int nargs = 2; + + // Push the Lua wrappers + PushSBClass(L, &sb_frame); + PushSBClass(L, &sb_bp_loc); + + // Call into the Lua callback passing 'sb_frame' and 'sb_wp'. + if (lua_pcall(L, nargs, 1, 0) != LUA_OK) { + luaL_error(L, "%s", lua_tostring(L, -1)); + return true; + } + + // Boolean return from the callback + bool stop = lua_toboolean(L, -1); + lua_pop(L, 1); + + return stop; +} + %} diff --git a/lldb/include/lldb/API/SBBreakpoint.h b/lldb/include/lldb/API/SBBreakpoint.h --- a/lldb/include/lldb/API/SBBreakpoint.h +++ b/lldb/include/lldb/API/SBBreakpoint.h @@ -9,6 +9,7 @@ #ifndef LLDB_API_SBBREAKPOINT_H #define LLDB_API_SBBREAKPOINT_H +#include "lldb/API/SBBreakpointOptionCommon.h" #include "lldb/API/SBDefines.h" class SBBreakpointListImpl; @@ -94,6 +95,8 @@ void SetCallback(SBBreakpointHitCallback callback, void *baton); + void SetScriptCallback(const SBBreakpointCallbackBatonSP &baton_sp); + void SetScriptCallbackFunction(const char *callback_function_name); SBError SetScriptCallbackFunction(const char *callback_function_name, diff --git a/lldb/include/lldb/API/SBBreakpointOptionCommon.h b/lldb/include/lldb/API/SBBreakpointOptionCommon.h new file mode 100644 --- /dev/null +++ b/lldb/include/lldb/API/SBBreakpointOptionCommon.h @@ -0,0 +1,40 @@ +//===-- SBBreakpointOptionCommon.h ------------------------------------------*- +// C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_API_SBBREAKPOINTOPTIONCOMMON_H +#define LLDB_API_SBBREAKPOINTOPTIONCOMMON_H + +#include "lldb/API/SBDefines.h" +#include "lldb/Utility/Baton.h" + +namespace lldb { +struct CallbackData { + SBBreakpointHitCallback callback; + void *callback_baton; + + virtual ~CallbackData() = default; +}; + +class SBBreakpointCallbackBaton + : public lldb_private::TypedBaton { +public: + explicit SBBreakpointCallbackBaton(std::unique_ptr Data); + SBBreakpointCallbackBaton(SBBreakpointHitCallback callback, void *baton); + + ~SBBreakpointCallbackBaton() override; + + static bool PrivateBreakpointHitCallback( + void *baton, lldb_private::StoppointCallbackContext *ctx, + lldb::user_id_t break_id, lldb::user_id_t break_loc_id); +}; + +typedef std::shared_ptr SBBreakpointCallbackBatonSP; + +} // namespace lldb +#endif // LLDB_API_SBBREAKPOINTOPTIONCOMMON_H diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h --- a/lldb/include/lldb/API/SBDefines.h +++ b/lldb/include/lldb/API/SBDefines.h @@ -94,9 +94,8 @@ class LLDB_API SBWatchpoint; class LLDB_API SBUnixSignals; -typedef bool (*SBBreakpointHitCallback)(void *baton, SBProcess &process, - SBThread &thread, - lldb::SBBreakpointLocation &location); +typedef bool (*SBBreakpointHitCallback)(void *baton, SBFrame &sb_frame, + SBBreakpointLocation &sb_bp_loc); } #endif // LLDB_API_SBDEFINES_H diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp --- a/lldb/source/API/SBBreakpoint.cpp +++ b/lldb/source/API/SBBreakpoint.cpp @@ -9,6 +9,7 @@ #include "lldb/API/SBBreakpoint.h" #include "SBReproducerPrivate.h" #include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBBreakpointOptionCommon.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBProcess.h" @@ -36,8 +37,6 @@ #include "lldb/Target/ThreadSpec.h" #include "lldb/Utility/Stream.h" -#include "SBBreakpointOptionCommon.h" - #include "lldb/lldb-enumerations.h" #include "llvm/ADT/STLExtras.h" @@ -616,6 +615,21 @@ } } +void SBBreakpoint::SetScriptCallback(const SBBreakpointCallbackBatonSP &baton_sp) { + LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallback, + (const SBBreakpointCallbackBatonSP &), baton_sp); + + BreakpointSP bkpt_sp = GetSP(); + + if (bkpt_sp) { + std::lock_guard guard( + bkpt_sp->GetTarget().GetAPIMutex()); + bkpt_sp->SetCallback( + SBBreakpointCallbackBaton ::PrivateBreakpointHitCallback, baton_sp, + false); + } +} + void SBBreakpoint::SetScriptCallbackFunction( const char *callback_function_name) { LLDB_RECORD_METHOD(void, SBBreakpoint, SetScriptCallbackFunction, @@ -1045,6 +1059,8 @@ (lldb::SBAddress &)); LLDB_REGISTER_METHOD(lldb::SBStructuredData, SBBreakpoint, SerializeToStructuredData, ()); + LLDB_REGISTER_METHOD(void, SBBreakpoint, SetScriptCallback, + (const SBBreakpointCallbackBatonSP &)); LLDB_REGISTER_METHOD(void, SBBreakpoint, SetScriptCallbackFunction, (const char *)); LLDB_REGISTER_METHOD(lldb::SBError, SBBreakpoint, SetScriptCallbackFunction, diff --git a/lldb/source/API/SBBreakpointName.cpp b/lldb/source/API/SBBreakpointName.cpp --- a/lldb/source/API/SBBreakpointName.cpp +++ b/lldb/source/API/SBBreakpointName.cpp @@ -8,6 +8,7 @@ #include "lldb/API/SBBreakpointName.h" #include "SBReproducerPrivate.h" +#include "lldb/API/SBBreakpointOptionCommon.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBError.h" #include "lldb/API/SBStream.h" @@ -25,8 +26,6 @@ #include "lldb/Target/ThreadSpec.h" #include "lldb/Utility/Stream.h" -#include "SBBreakpointOptionCommon.h" - using namespace lldb; using namespace lldb_private; diff --git a/lldb/source/API/SBBreakpointOptionCommon.h b/lldb/source/API/SBBreakpointOptionCommon.h deleted file mode 100644 --- a/lldb/source/API/SBBreakpointOptionCommon.h +++ /dev/null @@ -1,36 +0,0 @@ -//===-- SBBreakpointOptionCommon.h ------------------------------------------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#ifndef LLDB_SOURCE_API_SBBREAKPOINTOPTIONCOMMON_H -#define LLDB_SOURCE_API_SBBREAKPOINTOPTIONCOMMON_H - -#include "lldb/API/SBDefines.h" -#include "lldb/Utility/Baton.h" - -namespace lldb -{ -struct CallbackData { - SBBreakpointHitCallback callback; - void *callback_baton; -}; - -class SBBreakpointCallbackBaton : public lldb_private::TypedBaton { -public: - SBBreakpointCallbackBaton(SBBreakpointHitCallback callback, - void *baton); - - ~SBBreakpointCallbackBaton() override; - - static bool PrivateBreakpointHitCallback(void *baton, - lldb_private::StoppointCallbackContext *ctx, - lldb::user_id_t break_id, - lldb::user_id_t break_loc_id); -}; - -} // namespace lldb -#endif // LLDB_SOURCE_API_SBBREAKPOINTOPTIONCOMMON_H diff --git a/lldb/source/API/SBBreakpointOptionCommon.cpp b/lldb/source/API/SBBreakpointOptionCommon.cpp --- a/lldb/source/API/SBBreakpointOptionCommon.cpp +++ b/lldb/source/API/SBBreakpointOptionCommon.cpp @@ -6,8 +6,9 @@ // //===----------------------------------------------------------------------===// -#include "lldb/API/SBBreakpointName.h" +#include "lldb/API/SBBreakpointOptionCommon.h" #include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBBreakpointName.h" #include "lldb/API/SBDebugger.h" #include "lldb/API/SBEvent.h" #include "lldb/API/SBProcess.h" @@ -31,13 +32,15 @@ #include "lldb/lldb-enumerations.h" -#include "SBBreakpointOptionCommon.h" - #include "llvm/ADT/STLExtras.h" using namespace lldb; using namespace lldb_private; +SBBreakpointCallbackBaton::SBBreakpointCallbackBaton( + std::unique_ptr Data) + : TypedBaton(std::move(Data)) {} + SBBreakpointCallbackBaton::SBBreakpointCallbackBaton(SBBreakpointHitCallback callback, void *baton) @@ -52,26 +55,18 @@ lldb::user_id_t break_loc_id) { ExecutionContext exe_ctx(ctx->exe_ctx_ref); + SBFrame sb_frame(exe_ctx.GetFrameSP()); BreakpointSP bp_sp( exe_ctx.GetTargetRef().GetBreakpointList().FindBreakpointByID(break_id)); if (baton && bp_sp) { CallbackData *data = (CallbackData *)baton; lldb_private::Breakpoint *bp = bp_sp.get(); - if (bp && data->callback) { - Process *process = exe_ctx.GetProcessPtr(); - if (process) { - SBProcess sb_process(process->shared_from_this()); - SBThread sb_thread; - SBBreakpointLocation sb_location; - assert(bp_sp); - sb_location.SetLocation(bp_sp->FindLocationByID(break_loc_id)); - Thread *thread = exe_ctx.GetThreadPtr(); - if (thread) - sb_thread.SetThread(thread->shared_from_this()); - return data->callback(data->callback_baton, sb_process, sb_thread, - sb_location); - } + if (bp && data->callback) { + SBBreakpointLocation sb_bp_loc; + assert(bp_sp); + sb_bp_loc.SetLocation(bp_sp->FindLocationByID(break_loc_id)); + return data->callback(data->callback_baton, sb_frame, sb_bp_loc); } } return true; // Return true if we should stop at this breakpoint diff --git a/lldb/test/API/lua_api/TestBreakpointAPI.lua b/lldb/test/API/lua_api/TestBreakpointAPI.lua --- a/lldb/test/API/lua_api/TestBreakpointAPI.lua +++ b/lldb/test/API/lua_api/TestBreakpointAPI.lua @@ -49,4 +49,20 @@ assertEquals(var_argc_value, 3) end +function _T:TestBreakpointRealCallback() + local target = self:create_target() + local breakpoint = target:BreakpointCreateByLocation('main.c', 31) + assertTrue(breakpoint:IsValid() and breakpoint:GetNumLocations() == 1) + local flag = false + local i = 1 + breakpoint:SetScriptCallback(function(frame) + flag = true + assertEquals(frame:FindVariable('i'):GetValueAsSigned(), i) + i = i + 1 + return false + end) + target:LaunchSimple(nil, nil, nil) + assertTrue(flag) +end + os.exit(_T:run())