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

Reply via email to