From 38dfb235db19caa1aaa58c1c8153a7464b932087 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 24 Feb 2021 11:04:47 -0800 Subject: [PATCH] [lldb] Support debugging utility functions LLDB uses utility functions to run code in the inferior for its own internal purposes, such as reading classes from the Objective-C runtime for example. Because these expressions should be transparent to the user, we ignore breakpoints and unwind the stack on errors, which makes them hard to debug. This patch adds a new setting target.debug-utility-expression that, when enabled, changes these options to facilitate debugging. It enables breakpoints, disables unwinding and writes out the utility function source code to disk so it shows up in the source view. Differential revision: https://reviews.llvm.org/D97249 --- lldb/include/lldb/Expression/UtilityFunction.h | 5 ++- lldb/include/lldb/Target/Target.h | 4 +++ lldb/source/Expression/FunctionCaller.cpp | 14 +++++--- lldb/source/Expression/UtilityFunction.cpp | 3 +- .../Clang/ClangExpressionSourceCode.cpp | 6 +++- .../Clang/ClangExpressionSourceCode.h | 1 + .../Clang/ClangUtilityFunction.cpp | 37 ++++++++++++++++------ .../ExpressionParser/Clang/ClangUtilityFunction.h | 5 ++- .../Plugins/TypeSystem/Clang/TypeSystemClang.cpp | 3 +- lldb/source/Target/Target.cpp | 11 +++++++ lldb/source/Target/TargetProperties.td | 3 ++ 11 files changed, 72 insertions(+), 20 deletions(-) diff --git a/lldb/include/lldb/Expression/UtilityFunction.h b/lldb/include/lldb/Expression/UtilityFunction.h index 99fb321..6b558b2 100644 --- a/lldb/include/lldb/Expression/UtilityFunction.h +++ b/lldb/include/lldb/Expression/UtilityFunction.h @@ -42,8 +42,11 @@ public: /// /// \param[in] name /// The name of the function, as used in the text. + /// + /// \param[in] enable_debugging + /// Enable debugging of this function. UtilityFunction(ExecutionContextScope &exe_scope, std::string text, - std::string name); + std::string name, bool enable_debugging); ~UtilityFunction() override; diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 69baefb..f35f4e9 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -227,6 +227,10 @@ public: void UpdateLaunchInfoFromProperties(); + void SetDebugUtilityExpression(bool debug); + + bool GetDebugUtilityExpression() const; + private: // Callbacks for m_launch_info. void Arg0ValueChangedCallback(); diff --git a/lldb/source/Expression/FunctionCaller.cpp b/lldb/source/Expression/FunctionCaller.cpp index f0abdb7..5f1eb24 100644 --- a/lldb/source/Expression/FunctionCaller.cpp +++ b/lldb/source/Expression/FunctionCaller.cpp @@ -317,12 +317,16 @@ lldb::ExpressionResults FunctionCaller::ExecuteFunction( lldb::ExpressionResults return_value = lldb::eExpressionSetupError; // FunctionCaller::ExecuteFunction execution is always just to get the - // result. Do make sure we ignore breakpoints, unwind on error, and don't try - // to debug it. + // result. Unless explicitly asked for, ignore breakpoints and unwind on + // error. + const bool enable_debugging = + exe_ctx.GetTargetPtr() && + exe_ctx.GetTargetPtr()->GetDebugUtilityExpression(); EvaluateExpressionOptions real_options = options; - real_options.SetDebug(false); - real_options.SetUnwindOnError(true); - real_options.SetIgnoreBreakpoints(true); + real_options.SetDebug(false); // This halts the expression for debugging. + real_options.SetGenerateDebugInfo(enable_debugging); + real_options.SetUnwindOnError(!enable_debugging); + real_options.SetIgnoreBreakpoints(!enable_debugging); lldb::addr_t args_addr; diff --git a/lldb/source/Expression/UtilityFunction.cpp b/lldb/source/Expression/UtilityFunction.cpp index 128db0c..7ad25f5 100644 --- a/lldb/source/Expression/UtilityFunction.cpp +++ b/lldb/source/Expression/UtilityFunction.cpp @@ -41,7 +41,8 @@ char UtilityFunction::ID; /// \param[in] name /// The name of the function, as used in the text. UtilityFunction::UtilityFunction(ExecutionContextScope &exe_scope, - std::string text, std::string name) + std::string text, std::string name, + bool enable_debugging) : Expression(exe_scope), m_execution_unit_sp(), m_jit_module_wp(), m_function_text(std::move(text)), m_function_name(std::move(name)) {} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp index 180e08b..59a2e87 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -31,6 +31,7 @@ using namespace lldb_private; #define PREFIX_NAME "" +#define SUFFIX_NAME "" const llvm::StringRef ClangExpressionSourceCode::g_prefix_file_name = PREFIX_NAME; @@ -73,6 +74,9 @@ extern "C" } )"; +const char *ClangExpressionSourceCode::g_expression_suffix = + "\n;\n#line 1 \"" SUFFIX_NAME "\"\n"; + namespace { class AddMacroState { @@ -180,7 +184,7 @@ lldb_private::ClangExpressionSourceCode::ClangExpressionSourceCode( // containing only the user expression. This will hide our wrapper code // from the user when we render diagnostics with Clang. m_start_marker = "#line 1 \"" + filename.str() + "\"\n"; - m_end_marker = "\n;\n#line 1 \"\"\n"; + m_end_marker = g_expression_suffix; } namespace { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h index 9a54f0e..54ae837 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h @@ -27,6 +27,7 @@ public: /// the user expression. static const llvm::StringRef g_prefix_file_name; static const char *g_expression_prefix; + static const char *g_expression_suffix; /// The possible ways an expression can be wrapped. enum class WrapKind { diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp index 9788a4e..5d56bd7 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp @@ -34,19 +34,36 @@ using namespace lldb_private; char ClangUtilityFunction::ID; -/// Constructor -/// -/// \param[in] text -/// The text of the function. Must be a full translation unit. -/// -/// \param[in] name -/// The name of the function, as used in the text. ClangUtilityFunction::ClangUtilityFunction(ExecutionContextScope &exe_scope, - std::string text, std::string name) + std::string text, std::string name, + bool enable_debugging) : UtilityFunction( exe_scope, - std::string(ClangExpressionSourceCode::g_expression_prefix) + text, - std::move(name)) {} + std::string(ClangExpressionSourceCode::g_expression_prefix) + text + + std::string(ClangExpressionSourceCode::g_expression_suffix), + std::move(name), enable_debugging) { + // Write the source code to a file so that LLDB's source manager can display + // it when debugging the code. + if (enable_debugging) { + int temp_fd = -1; + llvm::SmallString<128> result_path; + llvm::sys::fs::createTemporaryFile("lldb", "expr", temp_fd, result_path); + if (temp_fd != -1) { + lldb_private::NativeFile file(temp_fd, File::eOpenOptionWrite, true); + text = "#line 1 \"" + std::string(result_path) + "\"\n" + text; + size_t bytes_written = text.size(); + file.Write(text.c_str(), bytes_written); + if (bytes_written == text.size()) { + // If we successfully wrote the source to a temporary file, replace the + // function text with the next text containing the line directive. + m_function_text = + std::string(ClangExpressionSourceCode::g_expression_prefix) + text + + std::string(ClangExpressionSourceCode::g_expression_suffix); + } + file.Close(); + } + } +} ClangUtilityFunction::~ClangUtilityFunction() {} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h index 7914e14..785f86e 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h @@ -48,8 +48,11 @@ public: /// /// \param[in] name /// The name of the function, as used in the text. + /// + /// \param[in] enable_debugging + /// Enable debugging of this function. ClangUtilityFunction(ExecutionContextScope &exe_scope, std::string text, - std::string name); + std::string name, bool enable_debugging); ~ClangUtilityFunction() override; diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index e265157..c94cb8d 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -9718,7 +9718,8 @@ ScratchTypeSystemClang::CreateUtilityFunction(std::string text, return {}; return std::make_unique( - *target_sp.get(), std::move(text), std::move(name)); + *target_sp.get(), std::move(text), std::move(name), + target_sp->GetDebugUtilityExpression()); } PersistentExpressionState * diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index da47d8e..38e13e1 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -4307,6 +4307,17 @@ void TargetProperties::DisableSTDIOValueChangedCallback() { m_launch_info.GetFlags().Clear(lldb::eLaunchFlagDisableSTDIO); } +bool TargetProperties::GetDebugUtilityExpression() const { + const uint32_t idx = ePropertyDebugUtilityExpression; + return m_collection_sp->GetPropertyAtIndexAsBoolean( + nullptr, idx, g_target_properties[idx].default_uint_value != 0); +} + +void TargetProperties::SetDebugUtilityExpression(bool debug) { + const uint32_t idx = ePropertyDebugUtilityExpression; + m_collection_sp->SetPropertyAtIndexAsBoolean(nullptr, idx, debug); +} + // Target::TargetEventData Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp) diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td index 2d6a435..030c0a7 100644 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -172,6 +172,9 @@ let Definition = "target" in { def AutoInstallMainExecutable: Property<"auto-install-main-executable", "Boolean">, DefaultTrue, Desc<"Always install the main executable when connected to a remote platform.">; + def DebugUtilityExpression: Property<"debug-utility-expression", "Boolean">, + DefaultFalse, + Desc<"Enable debugging of LLDB-internal utility expressions.">; } let Definition = "process_experimental" in { -- 2.7.4