Index: lldb/include/lldb/Core/Debugger.h =================================================================== --- lldb/include/lldb/Core/Debugger.h +++ lldb/include/lldb/Core/Debugger.h @@ -387,6 +387,15 @@ /// Shared thread poll. Use only with ThreadPoolTaskGroup. static llvm::ThreadPool &GetThreadPool(); + /// Request an interrupt. + void RequestInterrupt(); + + /// Clear the request for an interrupt. + void ClearInterrupt(); + + /// Clear all interrupt requests. + static void ClearInterrupts(); + /// Report warning events. /// /// Progress events will be delivered to any debuggers that have listeners @@ -573,6 +582,8 @@ llvm::once_flag m_clear_once; lldb::TargetSP m_dummy_target_sp; + bool m_interrupt_requested; + // Events for m_sync_broadcaster enum { eBroadcastBitEventThreadIsListening = (1 << 0), Index: lldb/include/lldb/Utility/Instrumentation.h =================================================================== --- lldb/include/lldb/Utility/Instrumentation.h +++ lldb/include/lldb/Utility/Instrumentation.h @@ -81,6 +81,19 @@ Instrumenter(llvm::StringRef pretty_func, std::string &&pretty_args = {}); ~Instrumenter(); + using Callback = std::function; + + /// Register a callback that's called when entering the instrumentation + /// boundary. + static void RegisterEntryCallback(Callback callback); + + /// Register a callback that's called when leaving the instrumentation + /// boundary. + static void RegisterExitCallback(Callback callback); + + /// Unregister all entry and exit callbacks. + static void UnregisterCallbacks(); + private: void UpdateBoundary(); Index: lldb/source/Core/Debugger.cpp =================================================================== --- lldb/source/Core/Debugger.cpp +++ lldb/source/Core/Debugger.cpp @@ -45,6 +45,7 @@ #include "lldb/Target/ThreadList.h" #include "lldb/Utility/AnsiTerminal.h" #include "lldb/Utility/Event.h" +#include "lldb/Utility/Instrumentation.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Listener.h" #include "lldb/Utility/Log.h" @@ -542,6 +543,7 @@ g_debugger_list_ptr = new DebuggerList(); g_thread_pool = new llvm::ThreadPool(llvm::optimal_concurrency()); g_load_plugin_callback = load_plugin_callback; + instrumentation::Instrumenter::RegisterExitCallback([]() { Debugger::ClearInterrupts(); }); } void Debugger::Terminate() { @@ -1303,6 +1305,23 @@ std::make_shared(log_callback, baton); } +void Debugger::RequestInterrupt() { + m_interrupt_requested = true; +} + +void Debugger::ClearInterrupt() { + m_interrupt_requested = false; +} + +void Debugger::ClearInterrupts() { + if (g_debugger_list_ptr && g_debugger_list_mutex_ptr) { + std::lock_guard guard(*g_debugger_list_mutex_ptr); + DebuggerList::iterator pos, end = g_debugger_list_ptr->end(); + for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos) + (*pos)->ClearInterrupt(); + } +} + static void PrivateReportProgress(Debugger &debugger, uint64_t progress_id, const std::string &message, uint64_t completed, uint64_t total, Index: lldb/source/Utility/Instrumentation.cpp =================================================================== --- lldb/source/Utility/Instrumentation.cpp +++ lldb/source/Utility/Instrumentation.cpp @@ -23,12 +23,20 @@ // Instrument SB API calls with singposts when supported. static llvm::ManagedStatic g_api_signposts; +// Callbacks for entering the instrumentation boundary. +static std::vector g_entry_callbacks; + +// Callbacks for leaving the instrumentation boundary. +static std::vector g_exit_callbacks; + Instrumenter::Instrumenter(llvm::StringRef pretty_func, std::string &&pretty_args) : m_pretty_func(pretty_func) { if (!g_global_boundary) { g_global_boundary = true; m_local_boundary = true; + for (Callback callback : g_entry_callbacks) + callback(); g_api_signposts->startInterval(this, m_pretty_func); } LLDB_LOG(GetLog(LLDBLog::API), "[{0}] {1} ({2})", @@ -39,6 +47,21 @@ Instrumenter::~Instrumenter() { if (m_local_boundary) { g_global_boundary = false; + for (Callback callback : g_exit_callbacks) + callback(); g_api_signposts->endInterval(this, m_pretty_func); } } + +void Instrumenter::RegisterEntryCallback(Instrumenter::Callback callback) { + g_entry_callbacks.emplace_back(std::move(callback)); +} + +void Instrumenter::RegisterExitCallback(Instrumenter::Callback callback) { + g_exit_callbacks.emplace_back(std::move(callback)); +} + +void Instrumenter::UnregisterCallbacks() { + g_entry_callbacks.clear(); + g_exit_callbacks.clear(); +} Index: lldb/unittests/Utility/CMakeLists.txt =================================================================== --- lldb/unittests/Utility/CMakeLists.txt +++ lldb/unittests/Utility/CMakeLists.txt @@ -13,6 +13,7 @@ EventTest.cpp FileSpecTest.cpp FlagsTest.cpp + InstrumentationTest.cpp ListenerTest.cpp LogTest.cpp NameMatchesTest.cpp Index: lldb/unittests/Utility/InstrumentationTest.cpp =================================================================== --- /dev/null +++ lldb/unittests/Utility/InstrumentationTest.cpp @@ -0,0 +1,49 @@ +//===-- InstrumentationTest.cpp -------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "lldb/Utility/Instrumentation.h" + +using namespace llvm; +using namespace lldb_private; +using namespace lldb_private::instrumentation; + +TEST(InsturmentationTest, Callback) { + size_t entry = 0; + size_t exit = 0; + + Instrumenter::UnregisterCallbacks(); + + Instrumenter::RegisterEntryCallback([&]() { entry++; }); + Instrumenter::RegisterExitCallback([&]() { exit++; }); + + EXPECT_EQ(entry, 0UL); + EXPECT_EQ(exit, 0UL); + + { + Instrumenter foo("foo"); + EXPECT_EQ(entry, 1UL); + EXPECT_EQ(exit, 0UL); + } + + EXPECT_EQ(entry, 1UL); + EXPECT_EQ(exit, 1UL); + + { + Instrumenter bar("bar"); + EXPECT_EQ(entry, 2UL); + EXPECT_EQ(exit, 1UL); + } + + EXPECT_EQ(entry, 2UL); + EXPECT_EQ(exit, 2UL); + + Instrumenter::UnregisterCallbacks(); +}