JDevlieghere created this revision. JDevlieghere added a reviewer: jingham. Herald added a subscriber: pengfei. JDevlieghere requested review of this revision.
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. Here's what this looks like in action. I added a nullptr dereference to `__lldb_apple_objc_v2_get_shared_cache_class_info` and used `TestDataFormatterObjCNSContainer` as an example: $ lldb a.out (lldb) target create "a.out" Current executable set to 'a.out' (x86_64). (lldb) b main.m:782 Breakpoint 1: where = a.out`main + 11752 at main.m:783:4, address = 0x0000000100006838 (lldb) setting set target.debug-utility-expression true (lldb) r Process 77039 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x0000000100006838 a.out`main(argc=1, argv=0x00007ffeefbff500) at main.m:783:4 780 forKeyPath:@"atoms" 781 options:0 782 context:NULL]; // Set break point at this line. -> 783 [newMutableDictionary addObserver:[My_KVO_Observer new] 784 forKeyPath:@"weirdKeyToKVO" 785 options:NSKeyValueObservingOptionNew 786 context:NULL]; Process 77039 launched: 'a.out' (x86_64) (lldb) frame variable newArray nsDictionary newDictionary nscfDictionary cfDictionaryRef newMutableDictionary newMutableDictionaryRef cfarray_ref mutable_array_ref warning: could not execute support code to read Objective-C class data in the process. This may reduce the quality of type information available. Process 77039 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x0) frame #0: 0x00000001001df2de __lldb_apple_objc_v2_get_shared_cache_class_info`__lldb_apple_objc_v2_get_shared_cache_class_info(objc_opt_ro_ptr=0x00007fff2031bc08, class_infos_ptr=0x000000010063b000, class_infos_byte_size=1572864, should_log=0) at lldb-9f30fe.expr:68 65 uint32_t class_infos_byte_size, 66 uint32_t should_log) 67 { -> 68 int *i = 0; 69 *i = 10; 70 uint32_t idx = 0; 71 DEBUG_PRINTF ("objc_opt_ro_ptr = %p\n", objc_opt_ro_ptr); https://reviews.llvm.org/D97249 Files: lldb/include/lldb/Expression/UtilityFunction.h lldb/include/lldb/Symbol/TypeSystem.h lldb/include/lldb/Target/Target.h lldb/source/Expression/FunctionCaller.cpp lldb/source/Expression/UtilityFunction.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h lldb/source/Symbol/TypeSystem.cpp lldb/source/Target/Target.cpp lldb/source/Target/TargetProperties.td
Index: lldb/source/Target/TargetProperties.td =================================================================== --- lldb/source/Target/TargetProperties.td +++ 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 { Index: lldb/source/Target/Target.cpp =================================================================== --- lldb/source/Target/Target.cpp +++ lldb/source/Target/Target.cpp @@ -2303,8 +2303,8 @@ return type_system_or_err.takeError(); std::unique_ptr<UtilityFunction> utility_fn = - type_system_or_err->CreateUtilityFunction(std::move(expression), - std::move(name)); + type_system_or_err->CreateUtilityFunction( + std::move(expression), std::move(name), GetDebugUtilityExpression()); if (!utility_fn) return llvm::make_error<llvm::StringError>( llvm::StringRef("Could not create an expression for language") + @@ -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) Index: lldb/source/Symbol/TypeSystem.cpp =================================================================== --- lldb/source/Symbol/TypeSystem.cpp +++ lldb/source/Symbol/TypeSystem.cpp @@ -172,7 +172,8 @@ } std::unique_ptr<UtilityFunction> -TypeSystem::CreateUtilityFunction(std::string text, std::string name) { +TypeSystem::CreateUtilityFunction(std::string text, std::string name, + bool debug) { return {}; } Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -1186,8 +1186,9 @@ const ValueList &arg_value_list, const char *name) override; - std::unique_ptr<UtilityFunction> - CreateUtilityFunction(std::string text, std::string name) override; + std::unique_ptr<UtilityFunction> CreateUtilityFunction(std::string text, + std::string name, + bool debug) override; PersistentExpressionState *GetPersistentExpressionState() override; Index: lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp =================================================================== --- lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -9712,13 +9712,13 @@ std::unique_ptr<UtilityFunction> ScratchTypeSystemClang::CreateUtilityFunction(std::string text, - std::string name) { + std::string name, bool debug) { TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return {}; return std::make_unique<ClangUtilityFunction>( - *target_sp.get(), std::move(text), std::move(name)); + *target_sp.get(), std::move(text), std::move(name), debug); } PersistentExpressionState * Index: lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp =================================================================== --- lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -172,7 +172,6 @@ // Testing using the new C++11 raw string literals. If this breaks GCC then we // will need to revert to the code above... static const char *g_get_shared_cache_class_info_body = R"( - extern "C" { size_t strlen(const char *); @@ -1592,9 +1591,8 @@ if (objc_runtime) { for (lldb::ModuleSP mod_sp : process->GetTarget().GetImages().Modules()) { if (objc_runtime->IsModuleObjCLibrary(mod_sp)) { - const Symbol *symbol = - mod_sp->FindFirstSymbolWithNameAndType(g_class_getNameRaw_symbol_name, - lldb::eSymbolTypeCode); + const Symbol *symbol = mod_sp->FindFirstSymbolWithNameAndType( + g_class_getNameRaw_symbol_name, lldb::eSymbolTypeCode); if (symbol && (symbol->ValueIsAddress() || symbol->GetAddressRef().IsValid())) { class_name_getter_function_name = g_class_getNameRaw_symbol_name; Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h +++ lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.h @@ -49,7 +49,7 @@ /// \param[in] name /// The name of the function, as used in the text. ClangUtilityFunction(ExecutionContextScope &exe_scope, std::string text, - std::string name); + std::string name, bool debug = false); ~ClangUtilityFunction() override; Index: lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangUtilityFunction.cpp @@ -42,11 +42,31 @@ /// \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 debug) : UtilityFunction( exe_scope, std::string(ClangExpressionSourceCode::g_expression_prefix) + text, - std::move(name)) {} + std::move(name), debug) { + if (debug) { + 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; + } + file.Close(); + } + } +} ClangUtilityFunction::~ClangUtilityFunction() {} Index: lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp =================================================================== --- lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp +++ lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -35,7 +35,7 @@ const llvm::StringRef ClangExpressionSourceCode::g_prefix_file_name = PREFIX_NAME; const char *ClangExpressionSourceCode::g_expression_prefix = -"#line 1 \"" PREFIX_NAME R"(" + "#line 1 \"" PREFIX_NAME R"("#line 1 #ifndef offsetof #define offsetof(t, d) __builtin_offsetof(t, d) #endif Index: lldb/source/Expression/UtilityFunction.cpp =================================================================== --- lldb/source/Expression/UtilityFunction.cpp +++ lldb/source/Expression/UtilityFunction.cpp @@ -41,7 +41,7 @@ /// \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 debug) : Expression(exe_scope), m_execution_unit_sp(), m_jit_module_wp(), m_function_text(std::move(text)), m_function_name(std::move(name)) {} Index: lldb/source/Expression/FunctionCaller.cpp =================================================================== --- lldb/source/Expression/FunctionCaller.cpp +++ lldb/source/Expression/FunctionCaller.cpp @@ -100,8 +100,7 @@ jit_file.GetFilename() = const_func_name; jit_module_sp->SetFileSpecAndObjectName(jit_file, ConstString()); m_jit_module_wp = jit_module_sp; - process->GetTarget().GetImages().Append(jit_module_sp, - true /* notify */); + process->GetTarget().GetImages().Append(jit_module_sp, true /* notify */); } } if (process && m_jit_start_addr) @@ -317,12 +316,15 @@ 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 debug = 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.SetGenerateDebugInfo(debug); + real_options.SetUnwindOnError(!debug); + real_options.SetIgnoreBreakpoints(!debug); lldb::addr_t args_addr; Index: lldb/include/lldb/Target/Target.h =================================================================== --- lldb/include/lldb/Target/Target.h +++ 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(); Index: lldb/include/lldb/Symbol/TypeSystem.h =================================================================== --- lldb/include/lldb/Symbol/TypeSystem.h +++ lldb/include/lldb/Symbol/TypeSystem.h @@ -470,7 +470,7 @@ } virtual std::unique_ptr<UtilityFunction> - CreateUtilityFunction(std::string text, std::string name); + CreateUtilityFunction(std::string text, std::string name, bool debug = false); virtual PersistentExpressionState *GetPersistentExpressionState() { return nullptr; Index: lldb/include/lldb/Expression/UtilityFunction.h =================================================================== --- lldb/include/lldb/Expression/UtilityFunction.h +++ lldb/include/lldb/Expression/UtilityFunction.h @@ -43,7 +43,7 @@ /// \param[in] name /// The name of the function, as used in the text. UtilityFunction(ExecutionContextScope &exe_scope, std::string text, - std::string name); + std::string name, bool debug); ~UtilityFunction() override;
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits