diff --git a/lldb/include/lldb/Expression/UtilityFunction.h b/lldb/include/lldb/Expression/UtilityFunction.h --- a/lldb/include/lldb/Expression/UtilityFunction.h +++ b/lldb/include/lldb/Expression/UtilityFunction.h @@ -42,8 +42,11 @@ /// /// \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 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -227,6 +227,10 @@ 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 --- a/lldb/source/Expression/FunctionCaller.cpp +++ b/lldb/source/Expression/FunctionCaller.cpp @@ -317,12 +317,16 @@ 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 --- a/lldb/source/Expression/UtilityFunction.cpp +++ b/lldb/source/Expression/UtilityFunction.cpp @@ -41,7 +41,8 @@ /// \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.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.h @@ -27,6 +27,7 @@ /// 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/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp --- 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 @@ } )"; +const char *ClangExpressionSourceCode::g_expression_suffix = + "\n;\n#line 1 \"" SUFFIX_NAME "\"\n"; + namespace { class AddMacroState { @@ -180,7 +184,7 @@ // 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/ClangUtilityFunction.h b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h @@ -48,8 +48,11 @@ /// /// \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/ExpressionParser/Clang/ClangUtilityFunction.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp @@ -34,19 +34,36 @@ 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/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -9718,7 +9718,8 @@ 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 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -4307,6 +4307,17 @@ 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 --- a/lldb/source/Target/TargetProperties.td +++ b/lldb/source/Target/TargetProperties.td @@ -172,6 +172,9 @@ 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 {