From 02dc8ac5e37acd189d250564254f6ea4b5900f3c Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 14 Nov 2025 15:33:51 -0800 Subject: [PATCH 01/16] [BoundsSafety][LLDB] Implement instrumentation plugin for -fbounds-safety soft traps This patch tries to upstream code landed downstream in https://github.com/swiftlang/llvm-project/pull/11835. This patch implements an instrumentation plugin for the `-fbounds-safety` soft trap mode first implemented in https://github.com/swiftlang/llvm-project/pull/11645 (rdar://158088757). That functionality isn't supported in upstream Clang yet, however the instrumented plugin can be compiled without issue so this patch tries to upstream it. The included tests are all disabled when the clang used for testing doesn't support `-fbounds-safety`. This means the tests will be skipped. However, it's fairly easy to point LLDB at a clang that does support `-fbounds-safety. I've done this and confirmed the tests pass. To use a custom clang the following can be done: * For API tests set the `LLDB_TEST_COMPILER` CMake cache variable to point to appropriate compiler. * For shell tests applying a patch like this can be used to set the appropriate compiler: ``` --- a/lldb/test/Shell/helper/toolchain.py +++ b/lldb/test/Shell/helper/toolchain.py @@ -271,6 +271,7 @@ def use_support_substitutions(config): if config.lldb_lit_tools_dir: additional_tool_dirs.append(config.lldb_lit_tools_dir) + config.environment['CLANG'] = '/path/to/clang' llvm_config.use_clang( ``` The current implementation of -fbounds-safety traps works by emitting calls to runtime functions intended to log the occurrence of a soft trap. While the user could just set a breakpoint of these functions the instrumentation plugin sets it automatically and provides several additional features: When debug info is available: * It adjusts the stop reason to be the reason for trapping. This is extracted from the artificial frame in the debug info (similar to -fbounds-safety hard traps). * It adjusts the selected frame to be the frame where the soft trap occurred. When debug info is not available: * For the `call-with-str` soft trap mode the soft trap reason is read from the first argument register. * For the `call-minimal` soft trap mode the stop reason is adjusted to note its a bounds check failure but does not give further information because none is available. * In this situation the selected frame is not adjusted because in this mode the user will be looking at assembly and adjusting the frame makes things confusing. This patch includes shell and api tests. The shell tests seemed like the best way to test behavior when debug info is missing because those tests make it easy to disable building with debug info completely. rdar://163230807 --- lldb/include/lldb/lldb-enumerations.h | 1 + .../BoundsSafety/CMakeLists.txt | 13 + .../InstrumentationRuntimeBoundsSafety.cpp | 491 ++++++++++++++++++ .../InstrumentationRuntimeBoundsSafety.h | 61 +++ .../InstrumentationRuntime/CMakeLists.txt | 1 + .../API/lang/BoundsSafety/soft_trap/Makefile | 10 + .../TestBoundsSafetyInstrumentationPlugin.py | 148 ++++++ .../API/lang/BoundsSafety/soft_trap/main.c | 10 + .../soft_trap/mockSoftTrapRuntime.c | 17 + .../boundsSafetyMockCallSoftTrapRuntime.c | 8 + .../Inputs/boundsSafetyMockSoftTrapRuntime.c | 15 + .../Inputs/boundsSafetySoftTraps.c | 12 + .../boundsSafetySoftTrapsMissingReason.c | 20 + .../boundssafety_soft_trap_call_minimal.test | 28 + ...soft_trap_call_minimal_missing_reason.test | 31 ++ ...ty_soft_trap_call_minimal_no_dbg_info.test | 30 ++ ...fety_soft_trap_call_minimal_no_plugin.test | 30 ++ .../boundssafety_soft_trap_call_str.test | 28 + ...oft_trap_call_with_str_missing_reason.test | 31 ++ ...y_soft_trap_call_with_str_no_dbg_info.test | 27 + ...ap_call_with_str_no_dbg_info_null_str.test | 33 ++ ...ety_soft_trap_call_with_str_no_plugin.test | 30 ++ 22 files changed, 1075 insertions(+) create mode 100644 lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/CMakeLists.txt create mode 100644 lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp create mode 100644 lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h create mode 100644 lldb/test/API/lang/BoundsSafety/soft_trap/Makefile create mode 100644 lldb/test/API/lang/BoundsSafety/soft_trap/TestBoundsSafetyInstrumentationPlugin.py create mode 100644 lldb/test/API/lang/BoundsSafety/soft_trap/main.c create mode 100644 lldb/test/API/lang/BoundsSafety/soft_trap/mockSoftTrapRuntime.c create mode 100644 lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyMockCallSoftTrapRuntime.c create mode 100644 lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyMockSoftTrapRuntime.c create mode 100644 lldb/test/Shell/BoundsSafety/Inputs/boundsSafetySoftTraps.c create mode 100644 lldb/test/Shell/BoundsSafety/Inputs/boundsSafetySoftTrapsMissingReason.c create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal.test create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_missing_reason.test create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_dbg_info.test create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_plugin.test create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_str.test create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_missing_reason.test create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info.test create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info_null_str.test create mode 100644 lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_plugin.test diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 1a7db8faecd94..79f22be1c95d3 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -542,6 +542,7 @@ enum InstrumentationRuntimeType { eInstrumentationRuntimeTypeMainThreadChecker = 0x0003, eInstrumentationRuntimeTypeSwiftRuntimeReporting = 0x0004, eInstrumentationRuntimeTypeLibsanitizersAsan = 0x0005, + eInstrumentationRuntimeTypeBoundsSafety = 0x0006, eNumInstrumentationRuntimeTypes }; diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/CMakeLists.txt b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/CMakeLists.txt new file mode 100644 index 0000000000000..adbd6c45e45af --- /dev/null +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/CMakeLists.txt @@ -0,0 +1,13 @@ +add_lldb_library(lldbPluginInstrumentationRuntimeBoundsSafety PLUGIN + InstrumentationRuntimeBoundsSafety.cpp + + LINK_LIBS + lldbBreakpoint + lldbCore + lldbSymbol + lldbTarget + lldbPluginInstrumentationRuntimeUtility + + CLANG_LIBS + clangCodeGen + ) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp new file mode 100644 index 0000000000000..788fe9867c36e --- /dev/null +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -0,0 +1,491 @@ +//===-- InstrumentationRuntimeBoundsSafety.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 "InstrumentationRuntimeBoundsSafety.h" + +#include "Plugins/Process/Utility/HistoryThread.h" +#include "lldb/Breakpoint/StoppointCallbackContext.h" +#include "lldb/Core/Debugger.h" +#include "lldb/Core/Module.h" +#include "lldb/Core/PluginManager.h" +#include "lldb/Symbol/Block.h" +#include "lldb/Symbol/Symbol.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/Symbol/Variable.h" +#include "lldb/Symbol/VariableList.h" +#include "lldb/Target/InstrumentationRuntimeStopInfo.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/SectionLoadList.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" +#include "lldb/Utility/RegisterValue.h" +#include "lldb/Utility/RegularExpression.h" +#include "clang/CodeGen/ModuleBuilder.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +LLDB_PLUGIN_DEFINE(InstrumentationRuntimeBoundsSafety) + +#define BOUNDS_SAFETY_SOFT_TRAP_MINIMAL "__bounds_safety_soft_trap" +#define BOUNDS_SAFETY_SOFT_TRAP_S "__bounds_safety_soft_trap_s" + +const std::vector &getBoundsSafetySoftTrapRuntimeFuncs() { + static std::vector Funcs = {BOUNDS_SAFETY_SOFT_TRAP_MINIMAL, + BOUNDS_SAFETY_SOFT_TRAP_S}; + + return Funcs; +} + +#define SOFT_TRAP_CATEGORY_PREFIX "Soft " +#define SOFT_TRAP_FALLBACK_CATEGORY \ + SOFT_TRAP_CATEGORY_PREFIX "Bounds check failed" + +class InstrumentationBoundsSafetyStopInfo : public StopInfo { +public: + ~InstrumentationBoundsSafetyStopInfo() override = default; + + lldb::StopReason GetStopReason() const override { + return lldb::eStopReasonInstrumentation; + } + + std::optional + GetSuggestedStackFrameIndex(bool inlined_stack) override { + return m_value; + } + + const char *GetDescription() override { return m_description.c_str(); } + + bool DoShouldNotify(Event *event_ptr) override { return true; } + + static lldb::StopInfoSP + CreateInstrumentationBoundsSafetyStopInfo(Thread &thread) { + return StopInfoSP(new InstrumentationBoundsSafetyStopInfo(thread)); + } + +private: + InstrumentationBoundsSafetyStopInfo(Thread &thread); + + std::pair, std::optional> + ComputeStopReasonAndSuggestedStackFrame(bool &warning_emitted_for_failure); + + std::pair> + ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( + lldb::StackFrameSP parent_sf, lldb::user_id_t debugger_id, + bool &warning_emitted_for_failure); + + std::pair, std::optional> + ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( + ThreadSP thread_sp, lldb::user_id_t debugger_id, + bool &warning_emitted_for_failure); +}; + +InstrumentationBoundsSafetyStopInfo::InstrumentationBoundsSafetyStopInfo( + Thread &thread) + : StopInfo(thread, 0) { + // No additional data describing the reason for stopping + m_extended_info = nullptr; + m_description = SOFT_TRAP_FALLBACK_CATEGORY; + + bool warning_emitted_for_failure = false; + auto [Description, MaybeSuggestedStackIndex] = + ComputeStopReasonAndSuggestedStackFrame(warning_emitted_for_failure); + if (Description) + m_description = Description.value(); + if (MaybeSuggestedStackIndex) + m_value = MaybeSuggestedStackIndex.value(); + + // Emit warning about the failure to compute the stop info if one wasn't + // already emitted + if ((!Description.has_value()) && !warning_emitted_for_failure) { + if (auto thread_sp = GetThread()) { + lldb::user_id_t debugger_id = + thread_sp->GetProcess()->GetTarget().GetDebugger().GetID(); + Debugger::ReportWarning( + "specific BoundsSafety trap reason could not be computed", + debugger_id); + } + } +} + +std::pair, std::optional> +InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( + bool &warning_emitted_for_failure) { + auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + ThreadSP thread_sp = GetThread(); + if (!thread_sp) { + LLDB_LOGF(log_category, "failed to get thread while stopped"); + return {}; + } + + lldb::user_id_t debugger_id = + thread_sp->GetProcess()->GetTarget().GetDebugger().GetID(); + + auto parent_sf = thread_sp->GetStackFrameAtIndex(1); + if (!parent_sf) { + LLDB_LOGF(log_category, "got nullptr when fetching stackframe at index 1"); + return {}; + } + + if (parent_sf->HasDebugInformation()) { + return ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( + parent_sf, debugger_id, warning_emitted_for_failure); + } + + // If the debug info is missing we can still get some information + // from the parameter in the soft trap runtime call. + return ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( + thread_sp, debugger_id, warning_emitted_for_failure); +} + +std::pair> +InstrumentationBoundsSafetyStopInfo:: + ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( + lldb::StackFrameSP parent_sf, lldb::user_id_t debugger_id, + bool &warning_emitted_for_failure) { + // First try to use debug info to understand the reason for trapping. The + // call stack will look something like this: + // + // ``` + // frame #0: `__bounds_safety_soft_trap_s(reason="") + // frame #1: `__clang_trap_msg$Bounds check failed$' + // frame #2: `bad_read(index=10) + // ``` + // .... + const auto *TrapReasonFuncName = parent_sf->GetFunctionName(); + + auto MaybeTrapReason = + clang::CodeGen::DemangleTrapReasonInDebugInfo(TrapReasonFuncName); + if (!MaybeTrapReason.has_value()) { + LLDB_LOGF( + GetLog(LLDBLog::InstrumentationRuntime), + "clang::CodeGen::DemangleTrapReasonInDebugInfo(\"%s\") call failed", + TrapReasonFuncName); + return {}; + } + auto category = MaybeTrapReason.value().first; + auto message = MaybeTrapReason.value().second; + + // TODO: Clang should probably be changed to emit the "Soft " prefix itself + std::string stop_reason; + llvm::raw_string_ostream ss(stop_reason); + ss << SOFT_TRAP_CATEGORY_PREFIX; + if (category.empty()) + ss << ""; + else + ss << category; + if (message.empty()) { + // This is not a failure so leave `warning_emitted_for_failure` untouched. + Debugger::ReportWarning( + "specific BoundsSafety trap reason is not " + "available because the compiler omitted it from the debug info", + debugger_id); + } else { + ss << ": " << message; + } + // Use computed stop-reason and assume the parent of `parent_sf` is the + // the place in the user's code where the call to the soft trap runtime + // originated. + return std::make_pair(stop_reason, parent_sf->GetFrameIndex() + 1); +} + +std::pair, std::optional> +InstrumentationBoundsSafetyStopInfo:: + ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( + ThreadSP thread_sp, lldb::user_id_t debugger_id, + bool &warning_emitted_for_failure) { + + auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + auto softtrap_sf = thread_sp->GetStackFrameAtIndex(0); + if (!softtrap_sf) { + LLDB_LOGF(log_category, "got nullptr when fetching stackframe at index 0"); + return {}; + } + llvm::StringRef trap_reason_func_name = softtrap_sf->GetFunctionName(); + + if (trap_reason_func_name == BOUNDS_SAFETY_SOFT_TRAP_MINIMAL) { + // This function has no arguments so there's no additional information + // that would allow us to identify the trap reason. + // + // Use the fallback stop reason and the current frame. + // While we "could" set the suggested frame to our parent (where the + // bounds check failed), doing this leads to very misleading output in + // LLDB. E.g.: + // + // ``` + // 0x100003b40 <+104>: bl 0x100003d64 ; __bounds_safety_soft_trap + // -> 0x100003b44 <+108>: b 0x100003b48 ; <+112> + // ``` + // + // This makes it look we stopped after finishing the call to + // `__bounds_safety_soft_trap` but actually we are in the middle of the + // call. To avoid this confusion just use the current frame. + Debugger::ReportWarning( + "specific BoundsSafety trap reason is not available because debug " + "info is missing on the caller of '" BOUNDS_SAFETY_SOFT_TRAP_MINIMAL + "'", + debugger_id); + warning_emitted_for_failure = true; + return {}; + } + + // BOUNDS_SAFETY_SOFT_TRAP_S has one argument which is a pointer to a string + // describing the trap or a nullptr. + if (trap_reason_func_name != BOUNDS_SAFETY_SOFT_TRAP_S) { + LLDB_LOGF(log_category, + "unexpected function name. Expected \"%s\" but got \"%s\"", + BOUNDS_SAFETY_SOFT_TRAP_S, trap_reason_func_name.data()); + assert(0 && "hit breakpoint for unexpected function name"); + return {}; + } + + auto rc = thread_sp->GetRegisterContext(); + if (!rc) { + LLDB_LOGF(log_category, "failed to get register context"); + return {}; + } + + // FIXME: LLDB should have an API that tells us for the current target if + // `LLDB_REGNUM_GENERIC_ARG1` can be used. + // https://github.com/llvm/llvm-project/issues/168602 + // Don't try for architectures where examining the first register won't + // work. + auto process = thread_sp->GetProcess(); + if (!process) { + LLDB_LOGF(log_category, "failed to get process"); + return {}; + } + switch (process->GetTarget().GetArchitecture().GetCore()) { + case ArchSpec::eCore_x86_32_i386: + case ArchSpec::eCore_x86_32_i486: + case ArchSpec::eCore_x86_32_i486sx: + case ArchSpec::eCore_x86_32_i686: + // Technically some x86 calling conventions do use a register for + // passing the first argument but let's ignore that for now. + Debugger::ReportWarning( + "specific BoundsSafety trap reason cannot be inferred on x86 when " + "the caller of '" BOUNDS_SAFETY_SOFT_TRAP_S "' is missing debug info", + debugger_id); + warning_emitted_for_failure = true; + return {}; + default: { + } + }; + + // Examine the register for the first argument + auto *arg0_info = rc->GetRegisterInfo( + lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); + if (!arg0_info) { + LLDB_LOGF(log_category, + "failed to get register info for LLDB_REGNUM_GENERIC_ARG1"); + return {}; + } + RegisterValue reg_value; + if (!rc->ReadRegister(arg0_info, reg_value)) { + LLDB_LOGF(log_category, "failed to read register %s", arg0_info->name); + return {}; + } + uint64_t reg_value_as_int = reg_value.GetAsUInt64(UINT64_MAX); + if (reg_value_as_int == UINT64_MAX) { + LLDB_LOGF(log_category, "failed to read register %s as a UInt64", + arg0_info->name); + return {}; + } + + if (reg_value_as_int == 0) { + // nullptr arg. The compiler will pass that if no trap reason string was + // available. + Debugger::ReportWarning( + "specific BoundsSafety trap reason cannot be inferred because the " + "compiler omitted the reason", + debugger_id); + warning_emitted_for_failure = true; + return {}; + } + + // The first argument to the call is a pointer to a global C string + // containing the trap reason. + std::string out_string; + Status error_status; + thread_sp->GetProcess()->ReadCStringFromMemory(reg_value_as_int, out_string, + error_status); + if (error_status.Fail()) { + LLDB_LOGF(log_category, "failed to read C string from address %p", + (void *)reg_value_as_int); + return {}; + } + LLDB_LOGF(log_category, "read C string from %p found in register %s: \"%s\"", + (void *)reg_value_as_int, arg0_info->name, out_string.c_str()); + std::string stop_reason; + llvm::raw_string_ostream SS(stop_reason); + SS << SOFT_TRAP_FALLBACK_CATEGORY; + if (!stop_reason.empty()) { + SS << ": " << out_string; + } + // Use the current frame as the suggested frame for the same reason as for + // `BOUNDS_SAFETY_SOFT_TRAP_MINIMAL`. + return {stop_reason, 0}; +} + +InstrumentationRuntimeBoundsSafety::~InstrumentationRuntimeBoundsSafety() { + Deactivate(); +} + +lldb::InstrumentationRuntimeSP +InstrumentationRuntimeBoundsSafety::CreateInstance( + const lldb::ProcessSP &process_sp) { + return InstrumentationRuntimeSP( + new InstrumentationRuntimeBoundsSafety(process_sp)); +} + +void InstrumentationRuntimeBoundsSafety::Initialize() { + PluginManager::RegisterPlugin(GetPluginNameStatic(), + "BoundsSafety instrumentation runtime plugin.", + CreateInstance, GetTypeStatic); +} + +void InstrumentationRuntimeBoundsSafety::Terminate() { + PluginManager::UnregisterPlugin(CreateInstance); +} + +lldb::InstrumentationRuntimeType +InstrumentationRuntimeBoundsSafety::GetTypeStatic() { + return lldb::eInstrumentationRuntimeTypeBoundsSafety; +} + +const RegularExpression & +InstrumentationRuntimeBoundsSafety::GetPatternForRuntimeLibrary() { + static RegularExpression regex; + return regex; +} + +bool InstrumentationRuntimeBoundsSafety::CheckIfRuntimeIsValid( + const lldb::ModuleSP module_sp) { + auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + for (const auto &SoftTrapFunc : getBoundsSafetySoftTrapRuntimeFuncs()) { + ConstString test_sym(SoftTrapFunc); + + if (module_sp->FindFirstSymbolWithNameAndType(test_sym, + lldb::eSymbolTypeAny)) { + LLDB_LOGF(log_category, "found \"%s\" in %s", SoftTrapFunc.c_str(), + module_sp->GetObjectName().AsCString("")); + return true; + } + } + LLDB_LOGF(log_category, + "did not findFound BoundsSafety soft trap functions in %s", + module_sp->GetObjectName().AsCString("")); + return false; +} + +bool InstrumentationRuntimeBoundsSafety::NotifyBreakpointHit( + void *baton, StoppointCallbackContext *context, user_id_t break_id, + user_id_t break_loc_id) { + assert(baton && "null baton"); + if (!baton) + return false; ///< false => resume execution. + + InstrumentationRuntimeBoundsSafety *const instance = + static_cast(baton); + + auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + ProcessSP process_sp = instance->GetProcessSP(); + if (!process_sp) { + LLDB_LOGF(log_category, "failed to get process from baton"); + return false; + } + ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); + if (!thread_sp) { + LLDB_LOGF(log_category, + "failed to get thread from StoppointCallbackContext"); + return false; + } + if (process_sp != context->exe_ctx_ref.GetProcessSP()) { + LLDB_LOGF(log_category, + "process from baton (%p) and StoppointCallbackContext (%p) do " + "not match", + (void *)process_sp.get(), + (void *)context->exe_ctx_ref.GetProcessSP().get()); + return false; + } + + if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) { + LLDB_LOGF(log_category, "IsLastResumeForUserExpression is true"); + return false; + } + + // Maybe the stop reason and stackframe selection should be done by + // a stackframe recognizer instead? + thread_sp->SetStopInfo( + InstrumentationBoundsSafetyStopInfo:: + CreateInstrumentationBoundsSafetyStopInfo(*thread_sp)); + return true; +} + +void InstrumentationRuntimeBoundsSafety::Activate() { + if (IsActive()) + return; + + auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + ProcessSP process_sp = GetProcessSP(); + if (!process_sp) { + LLDB_LOGF(log_category, "could not get process during Activate()"); + return; + } + + auto breakpoint = process_sp->GetTarget().CreateBreakpoint( + /*containingModules=*/nullptr, + /*containingSourceFiles=*/nullptr, getBoundsSafetySoftTrapRuntimeFuncs(), + eFunctionNameTypeFull, eLanguageTypeUnknown, + /*m_offset=*/0, + /*skip_prologue*/ eLazyBoolNo, + /*internal=*/true, + /*request_hardware*/ false); + + if (!breakpoint) + return; + if (!breakpoint->HasResolvedLocations()) { + LLDB_LOGF(log_category, + "breakpoint %d for BoundsSafety soft traps did not resolve to " + "any locations", + breakpoint->GetID()); + assert(0 && "breakpoint has no resolved locations"); + process_sp->GetTarget().RemoveBreakpointByID(breakpoint->GetID()); + return; + } + + // Note: When `sync=true` the suggested stackframe is completely ignored. So + // we use `sync=false`. Is that a bug? + breakpoint->SetCallback( + InstrumentationRuntimeBoundsSafety::NotifyBreakpointHit, this, + /*sync=*/false); + breakpoint->SetBreakpointKind("bounds-safety-soft-trap"); + SetBreakpointID(breakpoint->GetID()); + LLDB_LOGF(log_category, "created breakpoint %d for BoundsSafety soft traps", + breakpoint->GetID()); + SetActive(true); +} + +void InstrumentationRuntimeBoundsSafety::Deactivate() { + SetActive(false); + auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + if (ProcessSP process_sp = GetProcessSP()) { + bool success = + process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); + LLDB_LOGF(log_category, + "%sremoved breakpoint %llu for BoundsSafety soft traps", + success ? "" : "failed to ", GetBreakpointID()); + } else { + LLDB_LOGF(log_category, "no process available during Deactivate()"); + } + + SetBreakpointID(LLDB_INVALID_BREAK_ID); +} diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h new file mode 100644 index 0000000000000..425f9c583beaa --- /dev/null +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h @@ -0,0 +1,61 @@ +//===-- InstrumentationRuntimeBoundsSafetySoftTrap.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_PLUGINS_INSTRUMENTATIONRUNTIME_BOUNDS_SAFETY_SOFT_TRAP_H +#define LLDB_SOURCE_PLUGINS_INSTRUMENTATIONRUNTIME_BOUNDS_SAFETY_SOFT_TRAP_H + +#include "lldb/Target/ABI.h" +#include "lldb/Target/InstrumentationRuntime.h" +#include "lldb/Utility/StructuredData.h" +#include "lldb/lldb-private.h" + +namespace lldb_private { + +class InstrumentationRuntimeBoundsSafety + : public lldb_private::InstrumentationRuntime { +public: + ~InstrumentationRuntimeBoundsSafety() override; + + static lldb::InstrumentationRuntimeSP + CreateInstance(const lldb::ProcessSP &process_sp); + + static void Initialize(); + + static void Terminate(); + + static llvm::StringRef GetPluginNameStatic() { return "BoundsSafety"; } + + static lldb::InstrumentationRuntimeType GetTypeStatic(); + + llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); } + + virtual lldb::InstrumentationRuntimeType GetType() { return GetTypeStatic(); } + +private: + InstrumentationRuntimeBoundsSafety(const lldb::ProcessSP &process_sp) + : lldb_private::InstrumentationRuntime(process_sp) {} + + const RegularExpression &GetPatternForRuntimeLibrary() override; + + bool CheckIfRuntimeIsValid(const lldb::ModuleSP module_sp) override; + + void Activate() override; + + void Deactivate(); + + static bool NotifyBreakpointHit(void *baton, + StoppointCallbackContext *context, + lldb::user_id_t break_id, + lldb::user_id_t break_loc_id); + + bool MatchAllModules() override { return true; } +}; + +} // namespace lldb_private + +#endif diff --git a/lldb/source/Plugins/InstrumentationRuntime/CMakeLists.txt b/lldb/source/Plugins/InstrumentationRuntime/CMakeLists.txt index 2a6cf930945d1..b7e1a602f208f 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/CMakeLists.txt +++ b/lldb/source/Plugins/InstrumentationRuntime/CMakeLists.txt @@ -2,6 +2,7 @@ set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND InstrumentationRuntime) add_subdirectory(ASan) add_subdirectory(ASanLibsanitizers) +add_subdirectory(BoundsSafety) add_subdirectory(MainThreadChecker) add_subdirectory(TSan) add_subdirectory(UBSan) diff --git a/lldb/test/API/lang/BoundsSafety/soft_trap/Makefile b/lldb/test/API/lang/BoundsSafety/soft_trap/Makefile new file mode 100644 index 0000000000000..5e83e7ac6d93f --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/soft_trap/Makefile @@ -0,0 +1,10 @@ +# FIXME: mockSoftTrapRuntime.c shouldn't really be built with -fbounds-safety +C_SOURCES := main.c mockSoftTrapRuntime.c + +soft-trap-test-minimal: CFLAGS_EXTRAS := -fbounds-safety -fbounds-safety-soft-traps=call-minimal +soft-trap-test-minimal: all + +soft-trap-test-with-str: CFLAGS_EXTRAS := -fbounds-safety -fbounds-safety-soft-traps=call-with-str +soft-trap-test-with-str: all + +include Makefile.rules diff --git a/lldb/test/API/lang/BoundsSafety/soft_trap/TestBoundsSafetyInstrumentationPlugin.py b/lldb/test/API/lang/BoundsSafety/soft_trap/TestBoundsSafetyInstrumentationPlugin.py new file mode 100644 index 0000000000000..535a0bcf7c00e --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/soft_trap/TestBoundsSafetyInstrumentationPlugin.py @@ -0,0 +1,148 @@ +""" +Test the BoundsSafety instrumentation plugin +""" + +import lldb +from lldbsuite.test.lldbtest import * +from lldbsuite.test.decorators import * + + +STOP_REASON_MAX_LEN = 100 +SOFT_TRAP_FUNC_MINIMAL = "__bounds_safety_soft_trap" +SOFT_TRAP_FUNC_WITH_STR = "__bounds_safety_soft_trap_s" + + +class BoundsSafetyTestSoftTrapPlugin(TestBase): + def _check_stop_reason_impl( + self, + expected_soft_trap_func: str, + expected_stop_reason: str, + expected_func_name: str, + expected_file_name: str, + expected_line_num: int, + ): + process = self.test_target.process + thread = process.GetSelectedThread() + self.assertEqual( + thread.GetStopReason(), + lldb.eStopReasonInstrumentation, + ) + + stop_reason = thread.GetStopDescription(STOP_REASON_MAX_LEN) + self.assertEqual(stop_reason, expected_stop_reason) + + soft_trap_func_frame = thread.GetFrameAtIndex(0) + self.assertEqual(soft_trap_func_frame.name, expected_soft_trap_func) + + stop_frame = thread.GetSelectedFrame() + self.assertEqual(stop_frame.name, expected_func_name) + # The stop frame isn't frame 1 because that frame is the artificial + # frame containing the trap reason. + self.assertEqual(stop_frame.idx, 2) + file_name = stop_frame.GetLineEntry().GetFileSpec().basename + self.assertEqual(file_name, expected_file_name) + line = stop_frame.GetLineEntry().line + self.assertEqual(line, expected_line_num) + + def check_state_soft_trap_minimal( + self, stop_reason: str, func_name: str, file_name: str, line_num: int + ): + """ + Check the program state is as expected when hitting + a soft trap from -fbounds-safety-soft-traps=call-minimal + """ + self._check_stop_reason_impl( + SOFT_TRAP_FUNC_MINIMAL, + expected_stop_reason=stop_reason, + expected_func_name=func_name, + expected_file_name=file_name, + expected_line_num=line_num, + ) + + def check_state_soft_trap_with_str( + self, stop_reason: str, func_name: str, file_name: str, line_num: int + ): + """ + Check the program state is as expected when hitting + a soft trap from -fbounds-safety-soft-traps=call-with_str + """ + self._check_stop_reason_impl( + SOFT_TRAP_FUNC_WITH_STR, + expected_stop_reason=stop_reason, + expected_func_name=func_name, + expected_file_name=file_name, + expected_line_num=line_num, + ) + + # Skip the tests on Windows because they fail due to the stop reason + # being `eStopReasonNon` instead of the expected + # `eStopReasonInstrumentation`. + @skipIfWindows + @skipUnlessBoundsSafety + def test_call_minimal(self): + """ + Test the plugin on code built with + -fbounds-safety-soft-traps=call-minimal + """ + self.build(make_targets=["soft-trap-test-minimal"]) + self.test_target = self.createTestTarget() + self.runCmd("run") + + process = self.test_target.process + + # First soft trap hit + self.check_state_soft_trap_minimal( + "Soft Bounds check failed: indexing above upper bound in 'buffer[2]'", + "main", + "main.c", + 7, + ) + + process.Continue() + + # Second soft trap hit + self.check_state_soft_trap_minimal( + "Soft Bounds check failed: indexing below lower bound in 'buffer[-1]'", + "main", + "main.c", + 8, + ) + + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) + + @skipIfWindows + @skipUnlessBoundsSafety + def test_call_with_str(self): + """ + Test the plugin on code built with + -fbounds-safety-soft-traps=call-with-str + """ + self.build(make_targets=["soft-trap-test-with-str"]) + self.test_target = self.createTestTarget() + self.runCmd("run") + + process = self.test_target.process + + # First soft trap hit + self.check_state_soft_trap_with_str( + "Soft Bounds check failed: indexing above upper bound in 'buffer[2]'", + "main", + "main.c", + 7, + ) + + process.Continue() + + # Second soft trap hit + self.check_state_soft_trap_with_str( + "Soft Bounds check failed: indexing below lower bound in 'buffer[-1]'", + "main", + "main.c", + 8, + ) + + process.Continue() + self.assertEqual(process.GetState(), lldb.eStateExited) + self.assertEqual(process.GetExitStatus(), 0) diff --git a/lldb/test/API/lang/BoundsSafety/soft_trap/main.c b/lldb/test/API/lang/BoundsSafety/soft_trap/main.c new file mode 100644 index 0000000000000..518afaaa02e8c --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/soft_trap/main.c @@ -0,0 +1,10 @@ +#include + +int main(void) { + int pad; + int buffer[] = {0, 1}; + int pad2; + int tmp = buffer[2]; // access past upper bound + tmp = buffer[-1]; // access below lower bound + return 0; +} diff --git a/lldb/test/API/lang/BoundsSafety/soft_trap/mockSoftTrapRuntime.c b/lldb/test/API/lang/BoundsSafety/soft_trap/mockSoftTrapRuntime.c new file mode 100644 index 0000000000000..2cfbd24234eff --- /dev/null +++ b/lldb/test/API/lang/BoundsSafety/soft_trap/mockSoftTrapRuntime.c @@ -0,0 +1,17 @@ +#include +#include +#include + +#if __CLANG_BOUNDS_SAFETY_SOFT_TRAP_API_VERSION > 0 +#error API version changed +#endif + +// FIXME: The runtimes really shouldn't be built with `-fbounds-safety` in +// soft trap mode because of the risk of infinite recursion. However, +// there's currently no way to have source files built with different flags + +void __bounds_safety_soft_trap_s(const char *reason) { + printf("BoundsSafety check FAILED: message:\"%s\"\n", reason ? reason : ""); +} + +void __bounds_safety_soft_trap(void) { printf("BoundsSafety check FAILED\n"); } diff --git a/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyMockCallSoftTrapRuntime.c b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyMockCallSoftTrapRuntime.c new file mode 100644 index 0000000000000..698cf272386c2 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyMockCallSoftTrapRuntime.c @@ -0,0 +1,8 @@ +#include +#include + +int main(void) { + __bounds_safety_soft_trap_s(0); + printf("Execution continued\n"); + return 0; +} diff --git a/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyMockSoftTrapRuntime.c b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyMockSoftTrapRuntime.c new file mode 100644 index 0000000000000..3e680451efba2 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetyMockSoftTrapRuntime.c @@ -0,0 +1,15 @@ +#include +#include +#include + +#if __CLANG_BOUNDS_SAFETY_SOFT_TRAP_API_VERSION > 0 +#error API version changed +#endif + +#if __has_ptrcheck +#error Do not compile the runtime with -fbounds-safety enabled due to potential for infinite recursion +#endif + +void __bounds_safety_soft_trap_s(const char *reason) { printf("BoundsSafety check FAILED: message:\"%s\"\n", reason ? reason : ""); } + +void __bounds_safety_soft_trap(void) { printf("BoundsSafety check FAILED\n"); } diff --git a/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetySoftTraps.c b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetySoftTraps.c new file mode 100644 index 0000000000000..265e57e427b1f --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetySoftTraps.c @@ -0,0 +1,12 @@ +#include + +int bad_read(int index) { + int array[] = {0, 1, 2}; + return array[index]; +} + +int main(int argc, char **argv) { + bad_read(10); + printf("Execution continued\n"); + return 0; +} diff --git a/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetySoftTrapsMissingReason.c b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetySoftTrapsMissingReason.c new file mode 100644 index 0000000000000..db65c9d93d42f --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/Inputs/boundsSafetySoftTrapsMissingReason.c @@ -0,0 +1,20 @@ +#include +#include +#include +#include + +int bad_call(int *__counted_by(count) ptr, int count) {} + +int main(int argc, char **argv) { + const int num_bytes = sizeof(int) * 2; + int *array = (int *)malloc(num_bytes); + memset(array, 0, num_bytes); + + // The count argument is too large and will cause a trap. + // This code pattern is currently missing a trap reason (rdar://100346924) and + // so we can use it to test how `InstrumentationRuntimeBoundsSafety` handles + // this. + bad_call(array, 3); + printf("Execution continued\n"); + return 0; +} diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal.test new file mode 100644 index 0000000000000..9177a716d9a2e --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal.test @@ -0,0 +1,28 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -fbounds-safety -fbounds-safety-soft-traps=call-minimal -g -O0 %S/Inputs/boundsSafetySoftTraps.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s + +# This test relies on this plugin being enabled +plugin list instrumentation-runtime.BoundsSafety +# CHECK: [+] BoundsSafety + +run + +# CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed: indexing above upper bound in 'array[index]'{{$}} + +# Check that the `bad_read` frame is selected +bt +# CHECK: frame #{{.*}}`__bounds_safety_soft_trap{{$}} +# CHECK-NEXT: frame #{{.*}}`__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[index]' +# CHECK-NEXT: * frame #{{.*}}`bad_read(index=10) at boundsSafetySoftTraps.c:5 + +# Resume execution +c +# CHECK: BoundsSafety check FAILED +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_missing_reason.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_missing_reason.test new file mode 100644 index 0000000000000..44e1787234890 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_missing_reason.test @@ -0,0 +1,31 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -fbounds-safety -fbounds-safety-soft-traps=call-minimal -g -O0 %S/Inputs/boundsSafetySoftTrapsMissingReason.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out 2> %t.warnings | FileCheck %s +# Warnings are checked separately because their order in the output is not guaranteed. +# RUN: FileCheck --input-file=%t.warnings --check-prefix=WARN %s + +# This test relies on this plugin being enabled +plugin list instrumentation-runtime.BoundsSafety +# CHECK: [+] BoundsSafety + +run + +# CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed{{$}} +# WARN: warning: specific BoundsSafety trap reason is not available because the compiler omitted it from the debug info + +# Check that the `bad_read` frame is selected +bt +# CHECK: frame #{{.*}}`__bounds_safety_soft_trap{{$}} +# CHECK-NEXT: frame #{{.*}}`__clang_trap_msg$Bounds check failed$ +# CHECK-NEXT: * frame #{{.*}}`main({{.+}}) at boundsSafetySoftTrapsMissingReason.c:17 + +# Resume execution +c +# CHECK: BoundsSafety check FAILED +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_dbg_info.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_dbg_info.test new file mode 100644 index 0000000000000..3fb2f9c015cf9 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_dbg_info.test @@ -0,0 +1,30 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -fbounds-safety -fbounds-safety-soft-traps=call-minimal -O0 %S/Inputs/boundsSafetySoftTraps.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out 2> %t.warnings | FileCheck %s +# Warnings are checked separately because their order in the output is not guaranteed. +# RUN: FileCheck --input-file=%t.warnings --check-prefix=WARN %s + +# This test relies on this plugin being enabled +plugin list instrumentation-runtime.BoundsSafety +# CHECK: [+] BoundsSafety + +run + +# CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed{{$}} +# WARN: warning: specific BoundsSafety trap reason is not available because debug info is missing on the caller of '__bounds_safety_soft_trap' + +# Check that the `__bounds_safety_soft_trap` frame is selected +bt +# CHECK: * frame #{{.*}}`__bounds_safety_soft_trap{{$}} +# CHECK-NEXT: frame #{{.*}}`bad_read + +# Resume execution +c +# CHECK: BoundsSafety check FAILED +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_plugin.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_plugin.test new file mode 100644 index 0000000000000..4acc927b667ed --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_plugin.test @@ -0,0 +1,30 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -fbounds-safety -fbounds-safety-soft-traps=call-minimal -g -O0 %S/Inputs/boundsSafetySoftTraps.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s + +# Run without the plugin. A user might want to do this so they can set their +# own custom breakpoint with custom stopping behavior (e.g. stop after n hits). +plugin disable instrumentation-runtime.BoundsSafety +# CHECK: [-] BoundsSafety + +b __bounds_safety_soft_trap +run + +# CHECK: * thread #{{.*}} stop reason = breakpoint 1.1 + +# Check that reason for bounds check failing can be seen in the stacktrace +bt +# CHECK: * frame #{{.*}}`__bounds_safety_soft_trap{{$}} +# CHECK-NEXT: frame #{{.*}}`__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[index]' +# CHECK-NEXT: frame #{{.*}}`bad_read(index=10) at boundsSafetySoftTraps.c:5 + +# Resume execution +c +# CHECK: BoundsSafety check FAILED +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_str.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_str.test new file mode 100644 index 0000000000000..9964049d05757 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_str.test @@ -0,0 +1,28 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -fbounds-safety -fbounds-safety-soft-traps=call-with-str -g -O0 %S/Inputs/boundsSafetySoftTraps.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s + +# This test relies on this plugin being enabled +plugin list instrumentation-runtime.BoundsSafety +# CHECK: [+] BoundsSafety + +run + +# CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed: indexing above upper bound in 'array[index]'{{$}} + +# Check that the `bad_read` frame is selected +bt +# CHECK: frame #{{.*}}`__bounds_safety_soft_trap_s{{$}} +# CHECK-NEXT: frame #{{.*}}`__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[index]' +# CHECK-NEXT: * frame #{{.*}}`bad_read(index=10) at boundsSafetySoftTraps.c:5 + +# Resume execution +c +# CHECK: BoundsSafety check FAILED +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_missing_reason.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_missing_reason.test new file mode 100644 index 0000000000000..b151c7e9d27b9 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_missing_reason.test @@ -0,0 +1,31 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -fbounds-safety -fbounds-safety-soft-traps=call-with-str -g -O0 %S/Inputs/boundsSafetySoftTrapsMissingReason.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out 2> %t.warnings | FileCheck %s +# Warnings are checked separately because their order in the output is not guaranteed. +# RUN: FileCheck --input-file=%t.warnings --check-prefix=WARN %s + +# This test relies on this plugin being enabled +plugin list instrumentation-runtime.BoundsSafety +# CHECK: [+] BoundsSafety + +run + +# CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed{{$}} +# WARN: warning: specific BoundsSafety trap reason is not available because the compiler omitted it from the debug info + +# Check that the `bad_read` frame is selected +bt +# CHECK: frame #{{.*}}`__bounds_safety_soft_trap_s{{$}} +# CHECK-NEXT: frame #{{.*}}`__clang_trap_msg$Bounds check failed$ +# CHECK-NEXT: * frame #{{.*}}`main({{.+}}) at boundsSafetySoftTrapsMissingReason.c:17 + +# Resume execution +c +# CHECK: BoundsSafety check FAILED +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info.test new file mode 100644 index 0000000000000..3814bcfd7e3a8 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info.test @@ -0,0 +1,27 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -fbounds-safety -fbounds-safety-soft-traps=call-with-str -O0 %S/Inputs/boundsSafetySoftTraps.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s + +# This test relies on this plugin being enabled +plugin list instrumentation-runtime.BoundsSafety +# CHECK: [+] BoundsSafety + +run + +# CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed: indexing above upper bound in 'array[index]'{{$}} + +# Check that the `__bounds_safety_soft_trap_s` frame is selected +bt +# CHECK: * frame #{{.*}}`__bounds_safety_soft_trap_s{{$}} +# CHECK-NEXT: frame #{{.*}}`bad_read + +# Resume execution +c +# CHECK: BoundsSafety check FAILED +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info_null_str.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info_null_str.test new file mode 100644 index 0000000000000..1c8056a48f37c --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info_null_str.test @@ -0,0 +1,33 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockCallSoftTrapRuntime.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out 2> %t.warnings | FileCheck %s +# Warnings are checked separately because their order in the output is not guaranteed. +# RUN: FileCheck --input-file=%t.warnings --check-prefix=WARN %s + +# This test relies on this plugin being enabled +plugin list instrumentation-runtime.BoundsSafety +# CHECK: [+] BoundsSafety + +run + +# This exists to check that the instrumentation correctly handles +# `__bounds_safety_soft_trap_s()` being called with a nullptr argument. + +# CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed{{$}} +# WARN: warning: specific BoundsSafety trap reason cannot be inferred because the compiler omitted the reason + +# Check that the `__bounds_safety_soft_trap_s` frame is selected +bt +# CHECK: * frame #{{.*}}`__bounds_safety_soft_trap_s{{$}} +# CHECK-NEXT: frame #{{.*}}`main + +# Resume execution +c +# CHECK: BoundsSafety check FAILED +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_plugin.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_plugin.test new file mode 100644 index 0000000000000..ca2ca988446e0 --- /dev/null +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_plugin.test @@ -0,0 +1,30 @@ +# UNSUPPORTED: system-windows +# REQUIRES: clang-bounds-safety +# RUN: %clang_host -c -fbounds-safety -fbounds-safety-soft-traps=call-with-str -g -O0 %S/Inputs/boundsSafetySoftTraps.c -o %t.o +# Note: Building the runtime without debug info is intentional because this is the common case +# RUN: %clang_host -c -O0 %S/Inputs/boundsSafetyMockSoftTrapRuntime.c -o %t.softtrap_runtime.o +# RUN: %clang_host %t.o %t.softtrap_runtime.o -o %t.out +# RUN: %lldb -b -s %s %t.out | FileCheck %s + +# Run without the plugin. A user might want to do this so they can set their +# own custom breakpoint with custom stopping behavior (e.g. stop after n hits). +plugin disable instrumentation-runtime.BoundsSafety +# CHECK: [-] BoundsSafety + +b __bounds_safety_soft_trap_s +run + +# CHECK: * thread #{{.*}} stop reason = breakpoint 1.1 + +# Check that reason for bounds check failing can be seen in the stacktrace +bt +# CHECK: * frame #{{.*}}`__bounds_safety_soft_trap_s +# CHECK-NEXT: frame #{{.*}}`__clang_trap_msg$Bounds check failed$indexing above upper bound in 'array[index]' +# CHECK-NEXT: frame #{{.*}}`bad_read(index=10) at boundsSafetySoftTraps.c:5 + +# Resume execution +c +# CHECK: BoundsSafety check FAILED: message:"indexing above upper bound in 'array[index]'" +# CHECK-NEXT: Execution continued +# CHECK: Process {{[0-9]+}} exited with status = 0 +q From 3c040e77fe8d26981a0a85f1fc65a428004c2950 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 15:26:34 -0800 Subject: [PATCH 02/16] Update lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h Co-authored-by: Jonas Devlieghere --- .../BoundsSafety/InstrumentationRuntimeBoundsSafety.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h index 425f9c583beaa..06c30f8febca8 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.h @@ -1,4 +1,4 @@ -//===-- InstrumentationRuntimeBoundsSafetySoftTrap.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. From 06857675647ec5b00a8f9053bafe506fb2751fe4 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 15:26:42 -0800 Subject: [PATCH 03/16] Update lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp Co-authored-by: Jonas Devlieghere --- .../BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 788fe9867c36e..0315677f7869d 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -1,4 +1,4 @@ -//===-- InstrumentationRuntimeBoundsSafety.cpp -----------------------===// +//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. From 93984ff16ac2670e3e61a19f340cd7168bb8e429 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 15:27:12 -0800 Subject: [PATCH 04/16] Update lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp Co-authored-by: Jonas Devlieghere --- .../BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 0315677f7869d..d28e46972441b 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -91,7 +91,7 @@ class InstrumentationBoundsSafetyStopInfo : public StopInfo { InstrumentationBoundsSafetyStopInfo::InstrumentationBoundsSafetyStopInfo( Thread &thread) : StopInfo(thread, 0) { - // No additional data describing the reason for stopping + // No additional data describing the reason for stopping. m_extended_info = nullptr; m_description = SOFT_TRAP_FALLBACK_CATEGORY; From fe5879db8f4b01bfb8a90a58c1ab136631c88976 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 15:27:19 -0800 Subject: [PATCH 05/16] Update lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp Co-authored-by: Jonas Devlieghere --- .../BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index d28e46972441b..956f573aad36c 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -104,7 +104,7 @@ InstrumentationBoundsSafetyStopInfo::InstrumentationBoundsSafetyStopInfo( m_value = MaybeSuggestedStackIndex.value(); // Emit warning about the failure to compute the stop info if one wasn't - // already emitted + // already emitted. if ((!Description.has_value()) && !warning_emitted_for_failure) { if (auto thread_sp = GetThread()) { lldb::user_id_t debugger_id = From 35f852d65f775e78ad6308fa2d43527d7c7cad71 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 15:28:09 -0800 Subject: [PATCH 06/16] Update lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp Co-authored-by: Jonas Devlieghere --- .../BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 956f573aad36c..0ffa4ddb92dc4 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -135,10 +135,9 @@ InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( return {}; } - if (parent_sf->HasDebugInformation()) { + if (parent_sf->HasDebugInformation()) return ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( parent_sf, debugger_id, warning_emitted_for_failure); - } // If the debug info is missing we can still get some information // from the parameter in the soft trap runtime call. From 25d11a1e8094a0946893e4044572ebd66907b54c Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 15:28:18 -0800 Subject: [PATCH 07/16] Update lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp Co-authored-by: Jonas Devlieghere --- .../BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 0ffa4ddb92dc4..06262fb0023d9 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -279,7 +279,7 @@ InstrumentationBoundsSafetyStopInfo:: } }; - // Examine the register for the first argument + // Examine the register for the first argument. auto *arg0_info = rc->GetRegisterInfo( lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); if (!arg0_info) { From 31813fff7a962ceb151985d0e08ade90f529dbb8 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 16:26:54 -0800 Subject: [PATCH 08/16] Try to use `llvm::StringLiteral` --- .../InstrumentationRuntimeBoundsSafety.cpp | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 06262fb0023d9..8ff5917ec56b0 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -35,12 +35,14 @@ using namespace lldb_private; LLDB_PLUGIN_DEFINE(InstrumentationRuntimeBoundsSafety) -#define BOUNDS_SAFETY_SOFT_TRAP_MINIMAL "__bounds_safety_soft_trap" -#define BOUNDS_SAFETY_SOFT_TRAP_S "__bounds_safety_soft_trap_s" +constexpr llvm::StringLiteral + BoundsSafetySoftTrapMinimal("__bounds_safety_soft_trap"); +constexpr llvm::StringLiteral + BoundsSafetySoftTrapStr("__bounds_safety_soft_trap_s"); const std::vector &getBoundsSafetySoftTrapRuntimeFuncs() { - static std::vector Funcs = {BOUNDS_SAFETY_SOFT_TRAP_MINIMAL, - BOUNDS_SAFETY_SOFT_TRAP_S}; + static std::vector Funcs = {BoundsSafetySoftTrapMinimal.str(), + BoundsSafetySoftTrapStr.str()}; return Funcs; } @@ -210,7 +212,7 @@ InstrumentationBoundsSafetyStopInfo:: } llvm::StringRef trap_reason_func_name = softtrap_sf->GetFunctionName(); - if (trap_reason_func_name == BOUNDS_SAFETY_SOFT_TRAP_MINIMAL) { + if (trap_reason_func_name == BoundsSafetySoftTrapMinimal) { // This function has no arguments so there's no additional information // that would allow us to identify the trap reason. // @@ -227,21 +229,22 @@ InstrumentationBoundsSafetyStopInfo:: // This makes it look we stopped after finishing the call to // `__bounds_safety_soft_trap` but actually we are in the middle of the // call. To avoid this confusion just use the current frame. - Debugger::ReportWarning( - "specific BoundsSafety trap reason is not available because debug " - "info is missing on the caller of '" BOUNDS_SAFETY_SOFT_TRAP_MINIMAL - "'", - debugger_id); + std::string warning; + llvm::raw_string_ostream ss(warning); + ss << "specific BoundsSafety trap reason is not available because debug " + "info is missing on the caller of '" + << BoundsSafetySoftTrapMinimal << "'"; + Debugger::ReportWarning(warning.c_str(), debugger_id); warning_emitted_for_failure = true; return {}; } - // BOUNDS_SAFETY_SOFT_TRAP_S has one argument which is a pointer to a string + // __bounds_safety_soft_trap_s has one argument which is a pointer to a string // describing the trap or a nullptr. - if (trap_reason_func_name != BOUNDS_SAFETY_SOFT_TRAP_S) { + if (trap_reason_func_name != BoundsSafetySoftTrapStr) { LLDB_LOGF(log_category, "unexpected function name. Expected \"%s\" but got \"%s\"", - BOUNDS_SAFETY_SOFT_TRAP_S, trap_reason_func_name.data()); + BoundsSafetySoftTrapStr.data(), trap_reason_func_name.data()); assert(0 && "hit breakpoint for unexpected function name"); return {}; } @@ -266,15 +269,18 @@ InstrumentationBoundsSafetyStopInfo:: case ArchSpec::eCore_x86_32_i386: case ArchSpec::eCore_x86_32_i486: case ArchSpec::eCore_x86_32_i486sx: - case ArchSpec::eCore_x86_32_i686: + case ArchSpec::eCore_x86_32_i686: { // Technically some x86 calling conventions do use a register for // passing the first argument but let's ignore that for now. - Debugger::ReportWarning( - "specific BoundsSafety trap reason cannot be inferred on x86 when " - "the caller of '" BOUNDS_SAFETY_SOFT_TRAP_S "' is missing debug info", - debugger_id); + std::string warning; + llvm::raw_string_ostream ss(warning); + ss << "specific BoundsSafety trap reason cannot be inferred on x86 when " + "the caller of '" + << BoundsSafetySoftTrapStr << "' is missing debug info"; + Debugger::ReportWarning(warning.c_str(), debugger_id); warning_emitted_for_failure = true; return {}; + } default: { } }; @@ -330,7 +336,7 @@ InstrumentationBoundsSafetyStopInfo:: SS << ": " << out_string; } // Use the current frame as the suggested frame for the same reason as for - // `BOUNDS_SAFETY_SOFT_TRAP_MINIMAL`. + // `__bounds_safety_soft_trap`. return {stop_reason, 0}; } From 01b13c889fbbbd211d0d149dac6048b304236ffa Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 16:35:48 -0800 Subject: [PATCH 09/16] Remove use of `auto` in a bunch of places --- .../InstrumentationRuntimeBoundsSafety.cpp | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 8ff5917ec56b0..42c2237fe1e27 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -98,17 +98,17 @@ InstrumentationBoundsSafetyStopInfo::InstrumentationBoundsSafetyStopInfo( m_description = SOFT_TRAP_FALLBACK_CATEGORY; bool warning_emitted_for_failure = false; - auto [Description, MaybeSuggestedStackIndex] = + auto [MaybeDescription, MaybeSuggestedStackIndex] = ComputeStopReasonAndSuggestedStackFrame(warning_emitted_for_failure); - if (Description) - m_description = Description.value(); + if (MaybeDescription) + m_description = MaybeDescription.value(); if (MaybeSuggestedStackIndex) m_value = MaybeSuggestedStackIndex.value(); // Emit warning about the failure to compute the stop info if one wasn't // already emitted. - if ((!Description.has_value()) && !warning_emitted_for_failure) { - if (auto thread_sp = GetThread()) { + if ((!MaybeDescription.has_value()) && !warning_emitted_for_failure) { + if (ThreadSP thread_sp = GetThread()) { lldb::user_id_t debugger_id = thread_sp->GetProcess()->GetTarget().GetDebugger().GetID(); Debugger::ReportWarning( @@ -121,7 +121,7 @@ InstrumentationBoundsSafetyStopInfo::InstrumentationBoundsSafetyStopInfo( std::pair, std::optional> InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( bool &warning_emitted_for_failure) { - auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ThreadSP thread_sp = GetThread(); if (!thread_sp) { LLDB_LOGF(log_category, "failed to get thread while stopped"); @@ -131,7 +131,7 @@ InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( lldb::user_id_t debugger_id = thread_sp->GetProcess()->GetTarget().GetDebugger().GetID(); - auto parent_sf = thread_sp->GetStackFrameAtIndex(1); + StackFrameSP parent_sf = thread_sp->GetStackFrameAtIndex(1); if (!parent_sf) { LLDB_LOGF(log_category, "got nullptr when fetching stackframe at index 1"); return {}; @@ -161,7 +161,7 @@ InstrumentationBoundsSafetyStopInfo:: // frame #2: `bad_read(index=10) // ``` // .... - const auto *TrapReasonFuncName = parent_sf->GetFunctionName(); + const char *TrapReasonFuncName = parent_sf->GetFunctionName(); auto MaybeTrapReason = clang::CodeGen::DemangleTrapReasonInDebugInfo(TrapReasonFuncName); @@ -172,8 +172,8 @@ InstrumentationBoundsSafetyStopInfo:: TrapReasonFuncName); return {}; } - auto category = MaybeTrapReason.value().first; - auto message = MaybeTrapReason.value().second; + llvm::StringRef category = MaybeTrapReason.value().first; + llvm::StringRef message = MaybeTrapReason.value().second; // TODO: Clang should probably be changed to emit the "Soft " prefix itself std::string stop_reason; @@ -204,8 +204,8 @@ InstrumentationBoundsSafetyStopInfo:: ThreadSP thread_sp, lldb::user_id_t debugger_id, bool &warning_emitted_for_failure) { - auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); - auto softtrap_sf = thread_sp->GetStackFrameAtIndex(0); + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); + StackFrameSP softtrap_sf = thread_sp->GetStackFrameAtIndex(0); if (!softtrap_sf) { LLDB_LOGF(log_category, "got nullptr when fetching stackframe at index 0"); return {}; @@ -249,7 +249,7 @@ InstrumentationBoundsSafetyStopInfo:: return {}; } - auto rc = thread_sp->GetRegisterContext(); + RegisterContextSP rc = thread_sp->GetRegisterContext(); if (!rc) { LLDB_LOGF(log_category, "failed to get register context"); return {}; @@ -260,7 +260,7 @@ InstrumentationBoundsSafetyStopInfo:: // https://github.com/llvm/llvm-project/issues/168602 // Don't try for architectures where examining the first register won't // work. - auto process = thread_sp->GetProcess(); + ProcessSP process = thread_sp->GetProcess(); if (!process) { LLDB_LOGF(log_category, "failed to get process"); return {}; @@ -286,7 +286,7 @@ InstrumentationBoundsSafetyStopInfo:: }; // Examine the register for the first argument. - auto *arg0_info = rc->GetRegisterInfo( + const RegisterInfo *arg0_info = rc->GetRegisterInfo( lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); if (!arg0_info) { LLDB_LOGF(log_category, @@ -374,7 +374,7 @@ InstrumentationRuntimeBoundsSafety::GetPatternForRuntimeLibrary() { bool InstrumentationRuntimeBoundsSafety::CheckIfRuntimeIsValid( const lldb::ModuleSP module_sp) { - auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); for (const auto &SoftTrapFunc : getBoundsSafetySoftTrapRuntimeFuncs()) { ConstString test_sym(SoftTrapFunc); @@ -401,7 +401,7 @@ bool InstrumentationRuntimeBoundsSafety::NotifyBreakpointHit( InstrumentationRuntimeBoundsSafety *const instance = static_cast(baton); - auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ProcessSP process_sp = instance->GetProcessSP(); if (!process_sp) { LLDB_LOGF(log_category, "failed to get process from baton"); @@ -439,14 +439,14 @@ void InstrumentationRuntimeBoundsSafety::Activate() { if (IsActive()) return; - auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ProcessSP process_sp = GetProcessSP(); if (!process_sp) { LLDB_LOGF(log_category, "could not get process during Activate()"); return; } - auto breakpoint = process_sp->GetTarget().CreateBreakpoint( + BreakpointSP breakpoint = process_sp->GetTarget().CreateBreakpoint( /*containingModules=*/nullptr, /*containingSourceFiles=*/nullptr, getBoundsSafetySoftTrapRuntimeFuncs(), eFunctionNameTypeFull, eLanguageTypeUnknown, @@ -481,7 +481,7 @@ void InstrumentationRuntimeBoundsSafety::Activate() { void InstrumentationRuntimeBoundsSafety::Deactivate() { SetActive(false); - auto *log_category = GetLog(LLDBLog::InstrumentationRuntime); + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); if (ProcessSP process_sp = GetProcessSP()) { bool success = process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); From f8ce65f3f12fcd7cd153a28204c3279b00ecab6e Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Fri, 21 Nov 2025 16:57:43 -0800 Subject: [PATCH 10/16] Move to LLDB_LOG --- .../InstrumentationRuntimeBoundsSafety.cpp | 89 ++++++++++--------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 42c2237fe1e27..65a7f2457b312 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -124,7 +124,7 @@ InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ThreadSP thread_sp = GetThread(); if (!thread_sp) { - LLDB_LOGF(log_category, "failed to get thread while stopped"); + LLDB_LOG(log_category, "failed to get thread while stopped"); return {}; } @@ -133,7 +133,7 @@ InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( StackFrameSP parent_sf = thread_sp->GetStackFrameAtIndex(1); if (!parent_sf) { - LLDB_LOGF(log_category, "got nullptr when fetching stackframe at index 1"); + LLDB_LOG(log_category, "got nullptr when fetching stackframe at index 1"); return {}; } @@ -166,9 +166,9 @@ InstrumentationBoundsSafetyStopInfo:: auto MaybeTrapReason = clang::CodeGen::DemangleTrapReasonInDebugInfo(TrapReasonFuncName); if (!MaybeTrapReason.has_value()) { - LLDB_LOGF( + LLDB_LOG( GetLog(LLDBLog::InstrumentationRuntime), - "clang::CodeGen::DemangleTrapReasonInDebugInfo(\"%s\") call failed", + "clang::CodeGen::DemangleTrapReasonInDebugInfo(\"{0}\") call failed", TrapReasonFuncName); return {}; } @@ -207,7 +207,7 @@ InstrumentationBoundsSafetyStopInfo:: Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); StackFrameSP softtrap_sf = thread_sp->GetStackFrameAtIndex(0); if (!softtrap_sf) { - LLDB_LOGF(log_category, "got nullptr when fetching stackframe at index 0"); + LLDB_LOG(log_category, "got nullptr when fetching stackframe at index 0"); return {}; } llvm::StringRef trap_reason_func_name = softtrap_sf->GetFunctionName(); @@ -242,16 +242,16 @@ InstrumentationBoundsSafetyStopInfo:: // __bounds_safety_soft_trap_s has one argument which is a pointer to a string // describing the trap or a nullptr. if (trap_reason_func_name != BoundsSafetySoftTrapStr) { - LLDB_LOGF(log_category, - "unexpected function name. Expected \"%s\" but got \"%s\"", - BoundsSafetySoftTrapStr.data(), trap_reason_func_name.data()); + LLDB_LOG(log_category, + "unexpected function name. Expected \"{0}\" but got \"{1}\"", + BoundsSafetySoftTrapStr.data(), trap_reason_func_name.data()); assert(0 && "hit breakpoint for unexpected function name"); return {}; } RegisterContextSP rc = thread_sp->GetRegisterContext(); if (!rc) { - LLDB_LOGF(log_category, "failed to get register context"); + LLDB_LOG(log_category, "failed to get register context"); return {}; } @@ -262,7 +262,7 @@ InstrumentationBoundsSafetyStopInfo:: // work. ProcessSP process = thread_sp->GetProcess(); if (!process) { - LLDB_LOGF(log_category, "failed to get process"); + LLDB_LOG(log_category, "failed to get process"); return {}; } switch (process->GetTarget().GetArchitecture().GetCore()) { @@ -289,19 +289,19 @@ InstrumentationBoundsSafetyStopInfo:: const RegisterInfo *arg0_info = rc->GetRegisterInfo( lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); if (!arg0_info) { - LLDB_LOGF(log_category, - "failed to get register info for LLDB_REGNUM_GENERIC_ARG1"); + LLDB_LOG(log_category, + "failed to get register info for LLDB_REGNUM_GENERIC_ARG1"); return {}; } RegisterValue reg_value; if (!rc->ReadRegister(arg0_info, reg_value)) { - LLDB_LOGF(log_category, "failed to read register %s", arg0_info->name); + LLDB_LOG(log_category, "failed to read register {0}", arg0_info->name); return {}; } uint64_t reg_value_as_int = reg_value.GetAsUInt64(UINT64_MAX); if (reg_value_as_int == UINT64_MAX) { - LLDB_LOGF(log_category, "failed to read register %s as a UInt64", - arg0_info->name); + LLDB_LOG(log_category, "failed to read register {0} as a UInt64", + arg0_info->name); return {}; } @@ -323,12 +323,13 @@ InstrumentationBoundsSafetyStopInfo:: thread_sp->GetProcess()->ReadCStringFromMemory(reg_value_as_int, out_string, error_status); if (error_status.Fail()) { - LLDB_LOGF(log_category, "failed to read C string from address %p", - (void *)reg_value_as_int); + LLDB_LOG(log_category, "failed to read C string from address {0}", + (void *)reg_value_as_int); return {}; } - LLDB_LOGF(log_category, "read C string from %p found in register %s: \"%s\"", - (void *)reg_value_as_int, arg0_info->name, out_string.c_str()); + LLDB_LOG(log_category, + "read C string from {0} found in register {1}: \"{2}\"", + (void *)reg_value_as_int, arg0_info->name, out_string.c_str()); std::string stop_reason; llvm::raw_string_ostream SS(stop_reason); SS << SOFT_TRAP_FALLBACK_CATEGORY; @@ -380,14 +381,14 @@ bool InstrumentationRuntimeBoundsSafety::CheckIfRuntimeIsValid( if (module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny)) { - LLDB_LOGF(log_category, "found \"%s\" in %s", SoftTrapFunc.c_str(), - module_sp->GetObjectName().AsCString("")); + LLDB_LOG(log_category, "found \"{0}\" in {1}", SoftTrapFunc.c_str(), + module_sp->GetObjectName().AsCString("")); return true; } } - LLDB_LOGF(log_category, - "did not findFound BoundsSafety soft trap functions in %s", - module_sp->GetObjectName().AsCString("")); + LLDB_LOG(log_category, + "did not find BoundsSafety soft trap functions in module {0}", + module_sp->GetObjectName().AsCString("")); return false; } @@ -404,26 +405,26 @@ bool InstrumentationRuntimeBoundsSafety::NotifyBreakpointHit( Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ProcessSP process_sp = instance->GetProcessSP(); if (!process_sp) { - LLDB_LOGF(log_category, "failed to get process from baton"); + LLDB_LOG(log_category, "failed to get process from baton"); return false; } ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); if (!thread_sp) { - LLDB_LOGF(log_category, - "failed to get thread from StoppointCallbackContext"); + LLDB_LOG(log_category, + "failed to get thread from StoppointCallbackContext"); return false; } if (process_sp != context->exe_ctx_ref.GetProcessSP()) { - LLDB_LOGF(log_category, - "process from baton (%p) and StoppointCallbackContext (%p) do " - "not match", - (void *)process_sp.get(), - (void *)context->exe_ctx_ref.GetProcessSP().get()); + LLDB_LOG(log_category, + "process from baton ({0}) and StoppointCallbackContext ({1}) do " + "not match", + (void *)process_sp.get(), + (void *)context->exe_ctx_ref.GetProcessSP().get()); return false; } if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) { - LLDB_LOGF(log_category, "IsLastResumeForUserExpression is true"); + LLDB_LOG(log_category, "IsLastResumeForUserExpression is true"); return false; } @@ -442,7 +443,7 @@ void InstrumentationRuntimeBoundsSafety::Activate() { Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ProcessSP process_sp = GetProcessSP(); if (!process_sp) { - LLDB_LOGF(log_category, "could not get process during Activate()"); + LLDB_LOG(log_category, "could not get process during Activate()"); return; } @@ -458,10 +459,10 @@ void InstrumentationRuntimeBoundsSafety::Activate() { if (!breakpoint) return; if (!breakpoint->HasResolvedLocations()) { - LLDB_LOGF(log_category, - "breakpoint %d for BoundsSafety soft traps did not resolve to " - "any locations", - breakpoint->GetID()); + LLDB_LOG(log_category, + "breakpoint {0} for BoundsSafety soft traps did not resolve to " + "any locations", + breakpoint->GetID()); assert(0 && "breakpoint has no resolved locations"); process_sp->GetTarget().RemoveBreakpointByID(breakpoint->GetID()); return; @@ -474,8 +475,8 @@ void InstrumentationRuntimeBoundsSafety::Activate() { /*sync=*/false); breakpoint->SetBreakpointKind("bounds-safety-soft-trap"); SetBreakpointID(breakpoint->GetID()); - LLDB_LOGF(log_category, "created breakpoint %d for BoundsSafety soft traps", - breakpoint->GetID()); + LLDB_LOG(log_category, "created breakpoint {0} for BoundsSafety soft traps", + breakpoint->GetID()); SetActive(true); } @@ -485,11 +486,11 @@ void InstrumentationRuntimeBoundsSafety::Deactivate() { if (ProcessSP process_sp = GetProcessSP()) { bool success = process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); - LLDB_LOGF(log_category, - "%sremoved breakpoint %llu for BoundsSafety soft traps", - success ? "" : "failed to ", GetBreakpointID()); + LLDB_LOG(log_category, + "{0}removed breakpoint {1} for BoundsSafety soft traps", + success ? "" : "failed to ", GetBreakpointID()); } else { - LLDB_LOGF(log_category, "no process available during Deactivate()"); + LLDB_LOG(log_category, "no process available during Deactivate()"); } SetBreakpointID(LLDB_INVALID_BREAK_ID); From 9f7c1f14417e1dc48919daea68dc061b1d5c9245 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 22 Nov 2025 11:44:25 -0800 Subject: [PATCH 11/16] Formatting fix --- .../BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 65a7f2457b312..df9e81f161614 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -137,7 +137,7 @@ InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( return {}; } - if (parent_sf->HasDebugInformation()) + if (parent_sf->HasDebugInformation()) return ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( parent_sf, debugger_id, warning_emitted_for_failure); From 79fa92522377ba02c3aaa988bd4df7b1b8d76b81 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 1 Dec 2025 11:58:52 -0800 Subject: [PATCH 12/16] Introduce "LogBeforeReturn" templated function to simplify code that logs something and then returns. --- .../InstrumentationRuntimeBoundsSafety.cpp | 174 ++++++++---------- 1 file changed, 79 insertions(+), 95 deletions(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index df9e81f161614..352db95e35469 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -29,6 +29,7 @@ #include "clang/CodeGen/ModuleBuilder.h" #include +#include using namespace lldb; using namespace lldb_private; @@ -51,6 +52,9 @@ const std::vector &getBoundsSafetySoftTrapRuntimeFuncs() { #define SOFT_TRAP_FALLBACK_CATEGORY \ SOFT_TRAP_CATEGORY_PREFIX "Bounds check failed" +using ComputedStopInfo = + std::pair, std::optional>; + class InstrumentationBoundsSafetyStopInfo : public StopInfo { public: ~InstrumentationBoundsSafetyStopInfo() override = default; @@ -76,16 +80,14 @@ class InstrumentationBoundsSafetyStopInfo : public StopInfo { private: InstrumentationBoundsSafetyStopInfo(Thread &thread); - std::pair, std::optional> + ComputedStopInfo ComputeStopReasonAndSuggestedStackFrame(bool &warning_emitted_for_failure); - std::pair> - ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( + ComputedStopInfo ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( lldb::StackFrameSP parent_sf, lldb::user_id_t debugger_id, bool &warning_emitted_for_failure); - std::pair, std::optional> - ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( + ComputedStopInfo ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( ThreadSP thread_sp, lldb::user_id_t debugger_id, bool &warning_emitted_for_failure); }; @@ -118,24 +120,33 @@ InstrumentationBoundsSafetyStopInfo::InstrumentationBoundsSafetyStopInfo( } } -std::pair, std::optional> +// Helper functions to make it convenient to log a failure and then return. +template +[[nodiscard]] T LogBeforeReturn(ArgTys &&...Args) { + Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); + LLDB_LOG(log_category, Args...); + return T(); +} + +template +[[nodiscard]] ComputedStopInfo LogBeforeReturnCSI(ArgTys &&...Args) { + return LogBeforeReturn(Args...); +} + +ComputedStopInfo InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( bool &warning_emitted_for_failure) { - Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ThreadSP thread_sp = GetThread(); - if (!thread_sp) { - LLDB_LOG(log_category, "failed to get thread while stopped"); - return {}; - } + if (!thread_sp) + return LogBeforeReturnCSI("failed to get thread while stopped"); lldb::user_id_t debugger_id = thread_sp->GetProcess()->GetTarget().GetDebugger().GetID(); StackFrameSP parent_sf = thread_sp->GetStackFrameAtIndex(1); - if (!parent_sf) { - LLDB_LOG(log_category, "got nullptr when fetching stackframe at index 1"); - return {}; - } + if (!parent_sf) + return LogBeforeReturnCSI( + "got nullptr when fetching stackframe at index 1"); if (parent_sf->HasDebugInformation()) return ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( @@ -147,8 +158,7 @@ InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( thread_sp, debugger_id, warning_emitted_for_failure); } -std::pair> -InstrumentationBoundsSafetyStopInfo:: +ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( lldb::StackFrameSP parent_sf, lldb::user_id_t debugger_id, bool &warning_emitted_for_failure) { @@ -165,13 +175,11 @@ InstrumentationBoundsSafetyStopInfo:: auto MaybeTrapReason = clang::CodeGen::DemangleTrapReasonInDebugInfo(TrapReasonFuncName); - if (!MaybeTrapReason.has_value()) { - LLDB_LOG( - GetLog(LLDBLog::InstrumentationRuntime), + if (!MaybeTrapReason.has_value()) + return LogBeforeReturnCSI( "clang::CodeGen::DemangleTrapReasonInDebugInfo(\"{0}\") call failed", TrapReasonFuncName); - return {}; - } + llvm::StringRef category = MaybeTrapReason.value().first; llvm::StringRef message = MaybeTrapReason.value().second; @@ -198,18 +206,15 @@ InstrumentationBoundsSafetyStopInfo:: return std::make_pair(stop_reason, parent_sf->GetFrameIndex() + 1); } -std::pair, std::optional> -InstrumentationBoundsSafetyStopInfo:: +ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: ComputeStopReasonAndSuggestedStackFrameWithoutDebugInfo( ThreadSP thread_sp, lldb::user_id_t debugger_id, bool &warning_emitted_for_failure) { - Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); StackFrameSP softtrap_sf = thread_sp->GetStackFrameAtIndex(0); - if (!softtrap_sf) { - LLDB_LOG(log_category, "got nullptr when fetching stackframe at index 0"); - return {}; - } + if (!softtrap_sf) + return LogBeforeReturnCSI( + "got nullptr when fetching stackframe at index 0"); llvm::StringRef trap_reason_func_name = softtrap_sf->GetFunctionName(); if (trap_reason_func_name == BoundsSafetySoftTrapMinimal) { @@ -242,18 +247,15 @@ InstrumentationBoundsSafetyStopInfo:: // __bounds_safety_soft_trap_s has one argument which is a pointer to a string // describing the trap or a nullptr. if (trap_reason_func_name != BoundsSafetySoftTrapStr) { - LLDB_LOG(log_category, - "unexpected function name. Expected \"{0}\" but got \"{1}\"", - BoundsSafetySoftTrapStr.data(), trap_reason_func_name.data()); assert(0 && "hit breakpoint for unexpected function name"); - return {}; + return LogBeforeReturnCSI( + "unexpected function name. Expected \"{0}\" but got \"{1}\"", + BoundsSafetySoftTrapStr.data(), trap_reason_func_name.data()); } RegisterContextSP rc = thread_sp->GetRegisterContext(); - if (!rc) { - LLDB_LOG(log_category, "failed to get register context"); - return {}; - } + if (!rc) + return LogBeforeReturnCSI("failed to get register context"); // FIXME: LLDB should have an API that tells us for the current target if // `LLDB_REGNUM_GENERIC_ARG1` can be used. @@ -261,10 +263,9 @@ InstrumentationBoundsSafetyStopInfo:: // Don't try for architectures where examining the first register won't // work. ProcessSP process = thread_sp->GetProcess(); - if (!process) { - LLDB_LOG(log_category, "failed to get process"); - return {}; - } + if (!process) + return LogBeforeReturnCSI("failed to get process"); + switch (process->GetTarget().GetArchitecture().GetCore()) { case ArchSpec::eCore_x86_32_i386: case ArchSpec::eCore_x86_32_i486: @@ -288,22 +289,16 @@ InstrumentationBoundsSafetyStopInfo:: // Examine the register for the first argument. const RegisterInfo *arg0_info = rc->GetRegisterInfo( lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); - if (!arg0_info) { - LLDB_LOG(log_category, - "failed to get register info for LLDB_REGNUM_GENERIC_ARG1"); - return {}; - } + if (!arg0_info) + return LogBeforeReturnCSI( + "failed to get register info for LLDB_REGNUM_GENERIC_ARG1"); RegisterValue reg_value; - if (!rc->ReadRegister(arg0_info, reg_value)) { - LLDB_LOG(log_category, "failed to read register {0}", arg0_info->name); - return {}; - } + if (!rc->ReadRegister(arg0_info, reg_value)) + return LogBeforeReturnCSI("failed to read register {0}", arg0_info->name); uint64_t reg_value_as_int = reg_value.GetAsUInt64(UINT64_MAX); - if (reg_value_as_int == UINT64_MAX) { - LLDB_LOG(log_category, "failed to read register {0} as a UInt64", - arg0_info->name); - return {}; - } + if (reg_value_as_int == UINT64_MAX) + return LogBeforeReturnCSI("failed to read register {0} as a UInt64", + arg0_info->name); if (reg_value_as_int == 0) { // nullptr arg. The compiler will pass that if no trap reason string was @@ -322,12 +317,11 @@ InstrumentationBoundsSafetyStopInfo:: Status error_status; thread_sp->GetProcess()->ReadCStringFromMemory(reg_value_as_int, out_string, error_status); - if (error_status.Fail()) { - LLDB_LOG(log_category, "failed to read C string from address {0}", - (void *)reg_value_as_int); - return {}; - } - LLDB_LOG(log_category, + if (error_status.Fail()) + return LogBeforeReturnCSI("failed to read C string from address {0}", + (void *)reg_value_as_int); + + LLDB_LOG(GetLog(LLDBLog::InstrumentationRuntime), "read C string from {0} found in register {1}: \"{2}\"", (void *)reg_value_as_int, arg0_info->name, out_string.c_str()); std::string stop_reason; @@ -402,31 +396,23 @@ bool InstrumentationRuntimeBoundsSafety::NotifyBreakpointHit( InstrumentationRuntimeBoundsSafety *const instance = static_cast(baton); - Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ProcessSP process_sp = instance->GetProcessSP(); - if (!process_sp) { - LLDB_LOG(log_category, "failed to get process from baton"); - return false; - } + if (!process_sp) + return LogBeforeReturn("failed to get process from baton"); ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); - if (!thread_sp) { - LLDB_LOG(log_category, - "failed to get thread from StoppointCallbackContext"); - return false; - } - if (process_sp != context->exe_ctx_ref.GetProcessSP()) { - LLDB_LOG(log_category, - "process from baton ({0}) and StoppointCallbackContext ({1}) do " - "not match", - (void *)process_sp.get(), - (void *)context->exe_ctx_ref.GetProcessSP().get()); - return false; - } + if (!thread_sp) + return LogBeforeReturn( + "failed to get thread from StoppointCallbackContext"); - if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) { - LLDB_LOG(log_category, "IsLastResumeForUserExpression is true"); - return false; - } + if (process_sp != context->exe_ctx_ref.GetProcessSP()) + return LogBeforeReturn( + "process from baton ({0}) and StoppointCallbackContext ({1}) do " + "not match", + (void *)process_sp.get(), + (void *)context->exe_ctx_ref.GetProcessSP().get()); + + if (process_sp->GetModIDRef().IsLastResumeForUserExpression()) + return LogBeforeReturn("IsLastResumeForUserExpression is true"); // Maybe the stop reason and stackframe selection should be done by // a stackframe recognizer instead? @@ -440,12 +426,9 @@ void InstrumentationRuntimeBoundsSafety::Activate() { if (IsActive()) return; - Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); ProcessSP process_sp = GetProcessSP(); - if (!process_sp) { - LLDB_LOG(log_category, "could not get process during Activate()"); - return; - } + if (!process_sp) + return LogBeforeReturn("could not get process during Activate()"); BreakpointSP breakpoint = process_sp->GetTarget().CreateBreakpoint( /*containingModules=*/nullptr, @@ -457,15 +440,15 @@ void InstrumentationRuntimeBoundsSafety::Activate() { /*request_hardware*/ false); if (!breakpoint) - return; + return LogBeforeReturn("failed to create breakpoint"); + if (!breakpoint->HasResolvedLocations()) { - LLDB_LOG(log_category, - "breakpoint {0} for BoundsSafety soft traps did not resolve to " - "any locations", - breakpoint->GetID()); assert(0 && "breakpoint has no resolved locations"); process_sp->GetTarget().RemoveBreakpointByID(breakpoint->GetID()); - return; + return LogBeforeReturn( + "breakpoint {0} for BoundsSafety soft traps did not resolve to " + "any locations", + breakpoint->GetID()); } // Note: When `sync=true` the suggested stackframe is completely ignored. So @@ -475,7 +458,8 @@ void InstrumentationRuntimeBoundsSafety::Activate() { /*sync=*/false); breakpoint->SetBreakpointKind("bounds-safety-soft-trap"); SetBreakpointID(breakpoint->GetID()); - LLDB_LOG(log_category, "created breakpoint {0} for BoundsSafety soft traps", + LLDB_LOG(GetLog(LLDBLog::InstrumentationRuntime), + "created breakpoint {0} for BoundsSafety soft traps", breakpoint->GetID()); SetActive(true); } From 9c61b34c1bd9844f8c52edda98ffee172fac32f8 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 1 Dec 2025 13:03:33 -0800 Subject: [PATCH 13/16] Enable logging in shell tests so that there is coverage of at least some of the logging code while testing. --- .../BoundsSafety/boundssafety_soft_trap_call_minimal.test | 3 +++ .../boundssafety_soft_trap_call_minimal_missing_reason.test | 3 +++ .../boundssafety_soft_trap_call_minimal_no_dbg_info.test | 3 +++ .../Shell/BoundsSafety/boundssafety_soft_trap_call_str.test | 3 +++ .../boundssafety_soft_trap_call_with_str_missing_reason.test | 3 +++ .../boundssafety_soft_trap_call_with_str_no_dbg_info.test | 3 +++ ...ndssafety_soft_trap_call_with_str_no_dbg_info_null_str.test | 3 +++ 7 files changed, 21 insertions(+) diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal.test index 9177a716d9a2e..7e93f14a2672d 100644 --- a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal.test +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal.test @@ -10,6 +10,9 @@ plugin list instrumentation-runtime.BoundsSafety # CHECK: [+] BoundsSafety +# Emit logging so that the code has test coverage +log enable lldb instrumentation-runtime + run # CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed: indexing above upper bound in 'array[index]'{{$}} diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_missing_reason.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_missing_reason.test index 44e1787234890..a56144a196d63 100644 --- a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_missing_reason.test +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_missing_reason.test @@ -12,6 +12,9 @@ plugin list instrumentation-runtime.BoundsSafety # CHECK: [+] BoundsSafety +# Emit logging so that the code has test coverage +log enable lldb instrumentation-runtime + run # CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed{{$}} diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_dbg_info.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_dbg_info.test index 3fb2f9c015cf9..dfff65d5b5a8c 100644 --- a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_dbg_info.test +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_minimal_no_dbg_info.test @@ -12,6 +12,9 @@ plugin list instrumentation-runtime.BoundsSafety # CHECK: [+] BoundsSafety +# Emit logging so that the code has test coverage +log enable lldb instrumentation-runtime + run # CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed{{$}} diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_str.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_str.test index 9964049d05757..2fdae6d122733 100644 --- a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_str.test +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_str.test @@ -10,6 +10,9 @@ plugin list instrumentation-runtime.BoundsSafety # CHECK: [+] BoundsSafety +# Emit logging so that the code has test coverage +log enable lldb instrumentation-runtime + run # CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed: indexing above upper bound in 'array[index]'{{$}} diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_missing_reason.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_missing_reason.test index b151c7e9d27b9..68d10c00280ac 100644 --- a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_missing_reason.test +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_missing_reason.test @@ -12,6 +12,9 @@ plugin list instrumentation-runtime.BoundsSafety # CHECK: [+] BoundsSafety +# Emit logging so that the code has test coverage +log enable lldb instrumentation-runtime + run # CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed{{$}} diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info.test index 3814bcfd7e3a8..afe6098878822 100644 --- a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info.test +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info.test @@ -10,6 +10,9 @@ plugin list instrumentation-runtime.BoundsSafety # CHECK: [+] BoundsSafety +# Emit logging so that the code has test coverage +log enable lldb instrumentation-runtime + run # CHECK: * thread #{{.*}} stop reason = Soft Bounds check failed: indexing above upper bound in 'array[index]'{{$}} diff --git a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info_null_str.test b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info_null_str.test index 1c8056a48f37c..6e0bf325e7665 100644 --- a/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info_null_str.test +++ b/lldb/test/Shell/BoundsSafety/boundssafety_soft_trap_call_with_str_no_dbg_info_null_str.test @@ -12,6 +12,9 @@ plugin list instrumentation-runtime.BoundsSafety # CHECK: [+] BoundsSafety +# Emit logging so that the code has test coverage +log enable lldb instrumentation-runtime + run # This exists to check that the instrumentation correctly handles From 9e9c33d3289bfe23f3a5da454610fc3a376a7f18 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 1 Dec 2025 14:59:16 -0800 Subject: [PATCH 14/16] Fix nit --- .../BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index 352db95e35469..a9cc303384787 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -123,8 +123,7 @@ InstrumentationBoundsSafetyStopInfo::InstrumentationBoundsSafetyStopInfo( // Helper functions to make it convenient to log a failure and then return. template [[nodiscard]] T LogBeforeReturn(ArgTys &&...Args) { - Log *log_category = GetLog(LLDBLog::InstrumentationRuntime); - LLDB_LOG(log_category, Args...); + LLDB_LOG(GetLog(LLDBLog::InstrumentationRuntime), Args...); return T(); } From 9b4878b29a36d72d827c48ad77d942ecd422c19e Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 1 Dec 2025 15:30:20 -0800 Subject: [PATCH 15/16] Try to use constexpr std::array for `getBoundsSafetySoftTrapRuntimeFuncs` --- .../InstrumentationRuntimeBoundsSafety.cpp | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index a9cc303384787..ff85d7e17d403 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -41,11 +41,9 @@ constexpr llvm::StringLiteral constexpr llvm::StringLiteral BoundsSafetySoftTrapStr("__bounds_safety_soft_trap_s"); -const std::vector &getBoundsSafetySoftTrapRuntimeFuncs() { - static std::vector Funcs = {BoundsSafetySoftTrapMinimal.str(), - BoundsSafetySoftTrapStr.str()}; - - return Funcs; +constexpr std::array +getBoundsSafetySoftTrapRuntimeFuncs() { + return {BoundsSafetySoftTrapMinimal, BoundsSafetySoftTrapStr}; } #define SOFT_TRAP_CATEGORY_PREFIX "Soft " @@ -374,14 +372,15 @@ bool InstrumentationRuntimeBoundsSafety::CheckIfRuntimeIsValid( if (module_sp->FindFirstSymbolWithNameAndType(test_sym, lldb::eSymbolTypeAny)) { - LLDB_LOG(log_category, "found \"{0}\" in {1}", SoftTrapFunc.c_str(), - module_sp->GetObjectName().AsCString("")); + LLDB_LOG(log_category, "found \"{0}\" in {1}", + test_sym.AsCString(""), + module_sp->GetObjectName().AsCString("")); return true; } } LLDB_LOG(log_category, "did not find BoundsSafety soft trap functions in module {0}", - module_sp->GetObjectName().AsCString("")); + module_sp->GetObjectName().AsCString("")); return false; } @@ -429,10 +428,14 @@ void InstrumentationRuntimeBoundsSafety::Activate() { if (!process_sp) return LogBeforeReturn("could not get process during Activate()"); + std::vector breakpoints; + for (auto &breakpoint_func : getBoundsSafetySoftTrapRuntimeFuncs()) + breakpoints.emplace_back(breakpoint_func); + BreakpointSP breakpoint = process_sp->GetTarget().CreateBreakpoint( /*containingModules=*/nullptr, - /*containingSourceFiles=*/nullptr, getBoundsSafetySoftTrapRuntimeFuncs(), - eFunctionNameTypeFull, eLanguageTypeUnknown, + /*containingSourceFiles=*/nullptr, breakpoints, eFunctionNameTypeFull, + eLanguageTypeUnknown, /*m_offset=*/0, /*skip_prologue*/ eLazyBoolNo, /*internal=*/true, From 4eea0064f296d9f1b383293cd7004e4fffa46c13 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 1 Dec 2025 15:37:02 -0800 Subject: [PATCH 16/16] s/LogBeforeReturnCSI/LogFailedCSI/ --- .../InstrumentationRuntimeBoundsSafety.cpp | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp index ff85d7e17d403..db9b21305e938 100644 --- a/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp +++ b/lldb/source/Plugins/InstrumentationRuntime/BoundsSafety/InstrumentationRuntimeBoundsSafety.cpp @@ -126,7 +126,7 @@ template } template -[[nodiscard]] ComputedStopInfo LogBeforeReturnCSI(ArgTys &&...Args) { +[[nodiscard]] ComputedStopInfo LogFailedCSI(ArgTys &&...Args) { return LogBeforeReturn(Args...); } @@ -135,15 +135,14 @@ InstrumentationBoundsSafetyStopInfo::ComputeStopReasonAndSuggestedStackFrame( bool &warning_emitted_for_failure) { ThreadSP thread_sp = GetThread(); if (!thread_sp) - return LogBeforeReturnCSI("failed to get thread while stopped"); + return LogFailedCSI("failed to get thread while stopped"); lldb::user_id_t debugger_id = thread_sp->GetProcess()->GetTarget().GetDebugger().GetID(); StackFrameSP parent_sf = thread_sp->GetStackFrameAtIndex(1); if (!parent_sf) - return LogBeforeReturnCSI( - "got nullptr when fetching stackframe at index 1"); + return LogFailedCSI("got nullptr when fetching stackframe at index 1"); if (parent_sf->HasDebugInformation()) return ComputeStopReasonAndSuggestedStackFrameWithDebugInfo( @@ -173,7 +172,7 @@ ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: auto MaybeTrapReason = clang::CodeGen::DemangleTrapReasonInDebugInfo(TrapReasonFuncName); if (!MaybeTrapReason.has_value()) - return LogBeforeReturnCSI( + return LogFailedCSI( "clang::CodeGen::DemangleTrapReasonInDebugInfo(\"{0}\") call failed", TrapReasonFuncName); @@ -210,8 +209,7 @@ ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: StackFrameSP softtrap_sf = thread_sp->GetStackFrameAtIndex(0); if (!softtrap_sf) - return LogBeforeReturnCSI( - "got nullptr when fetching stackframe at index 0"); + return LogFailedCSI("got nullptr when fetching stackframe at index 0"); llvm::StringRef trap_reason_func_name = softtrap_sf->GetFunctionName(); if (trap_reason_func_name == BoundsSafetySoftTrapMinimal) { @@ -245,14 +243,14 @@ ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: // describing the trap or a nullptr. if (trap_reason_func_name != BoundsSafetySoftTrapStr) { assert(0 && "hit breakpoint for unexpected function name"); - return LogBeforeReturnCSI( + return LogFailedCSI( "unexpected function name. Expected \"{0}\" but got \"{1}\"", BoundsSafetySoftTrapStr.data(), trap_reason_func_name.data()); } RegisterContextSP rc = thread_sp->GetRegisterContext(); if (!rc) - return LogBeforeReturnCSI("failed to get register context"); + return LogFailedCSI("failed to get register context"); // FIXME: LLDB should have an API that tells us for the current target if // `LLDB_REGNUM_GENERIC_ARG1` can be used. @@ -261,7 +259,7 @@ ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: // work. ProcessSP process = thread_sp->GetProcess(); if (!process) - return LogBeforeReturnCSI("failed to get process"); + return LogFailedCSI("failed to get process"); switch (process->GetTarget().GetArchitecture().GetCore()) { case ArchSpec::eCore_x86_32_i386: @@ -287,15 +285,15 @@ ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: const RegisterInfo *arg0_info = rc->GetRegisterInfo( lldb::RegisterKind::eRegisterKindGeneric, LLDB_REGNUM_GENERIC_ARG1); if (!arg0_info) - return LogBeforeReturnCSI( + return LogFailedCSI( "failed to get register info for LLDB_REGNUM_GENERIC_ARG1"); RegisterValue reg_value; if (!rc->ReadRegister(arg0_info, reg_value)) - return LogBeforeReturnCSI("failed to read register {0}", arg0_info->name); + return LogFailedCSI("failed to read register {0}", arg0_info->name); uint64_t reg_value_as_int = reg_value.GetAsUInt64(UINT64_MAX); if (reg_value_as_int == UINT64_MAX) - return LogBeforeReturnCSI("failed to read register {0} as a UInt64", - arg0_info->name); + return LogFailedCSI("failed to read register {0} as a UInt64", + arg0_info->name); if (reg_value_as_int == 0) { // nullptr arg. The compiler will pass that if no trap reason string was @@ -315,8 +313,8 @@ ComputedStopInfo InstrumentationBoundsSafetyStopInfo:: thread_sp->GetProcess()->ReadCStringFromMemory(reg_value_as_int, out_string, error_status); if (error_status.Fail()) - return LogBeforeReturnCSI("failed to read C string from address {0}", - (void *)reg_value_as_int); + return LogFailedCSI("failed to read C string from address {0}", + (void *)reg_value_as_int); LLDB_LOG(GetLog(LLDBLog::InstrumentationRuntime), "read C string from {0} found in register {1}: \"{2}\"",