[Lldb-commits] [lldb] bbef51e - [lldb] make it easier to find LLDB's python

Lawrence D'Anna via lldb-commits lldb-commits at lists.llvm.org
Wed Nov 10 10:34:02 PST 2021


Author: Lawrence D'Anna
Date: 2021-11-10T10:33:34-08:00
New Revision: bbef51eb43c2e8f8e36fbbc0d0b4cca75b6f0863

URL: https://github.com/llvm/llvm-project/commit/bbef51eb43c2e8f8e36fbbc0d0b4cca75b6f0863
DIFF: https://github.com/llvm/llvm-project/commit/bbef51eb43c2e8f8e36fbbc0d0b4cca75b6f0863.diff

LOG: [lldb] make it easier to find LLDB's python

It is surprisingly difficult to write a simple python script that
can reliably `import lldb` without failing, or crashing.   I'm
currently resorting to convolutions like this:

    def find_lldb(may_reexec=False):
		if prefix := os.environ.get('LLDB_PYTHON_PREFIX'):
			if os.path.realpath(prefix) != os.path.realpath(sys.prefix):
				raise Exception("cannot import lldb.\n"
					f"  sys.prefix should be: {prefix}\n"
					f"  but it is: {sys.prefix}")
		else:
			line1, line2 = subprocess.run(
				['lldb', '-x', '-b', '-o', 'script print(sys.prefix)'],
				encoding='utf8', stdout=subprocess.PIPE,
				check=True).stdout.strip().splitlines()
			assert line1.strip() == '(lldb) script print(sys.prefix)'
			prefix = line2.strip()
			os.environ['LLDB_PYTHON_PREFIX'] = prefix

		if sys.prefix != prefix:
			if not may_reexec:
				raise Exception(
					"cannot import lldb.\n" +
					f"  This python, at {sys.prefix}\n"
					f"  does not math LLDB's python at {prefix}")
			os.environ['LLDB_PYTHON_PREFIX'] = prefix
			python_exe = os.path.join(prefix, 'bin', 'python3')
			os.execl(python_exe, python_exe, *sys.argv)

		lldb_path = subprocess.run(['lldb', '-P'],
			check=True, stdout=subprocess.PIPE,
				encoding='utf8').stdout.strip()

		sys.path = [lldb_path] + sys.path

This patch aims to replace all that with:

  #!/usr/bin/env lldb-python
  import lldb
  ...

... by adding the following features:

* new command line option: --print-script-interpreter-info.  This
   prints language-specific information about the script interpreter
   in JSON format.

* new tool (unix only): lldb-python which finds python and exec's it.

Reviewed By: JDevlieghere

Differential Revision: https://reviews.llvm.org/D112973

Added: 
    lldb/bindings/python/lldb-python

Modified: 
    lldb/bindings/interface/SBDebugger.i
    lldb/bindings/python/CMakeLists.txt
    lldb/docs/man/lldb.rst
    lldb/include/lldb/API/SBDebugger.h
    lldb/include/lldb/Interpreter/ScriptInterpreter.h
    lldb/source/API/SBDebugger.cpp
    lldb/source/Interpreter/ScriptInterpreter.cpp
    lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
    lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
    lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
    lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
    lldb/test/API/functionalities/paths/TestPaths.py
    lldb/test/Shell/Driver/TestHelp.test
    lldb/tools/driver/Driver.cpp
    lldb/tools/driver/Driver.h
    lldb/tools/driver/Options.td

Removed: 
    


################################################################################
diff  --git a/lldb/bindings/interface/SBDebugger.i b/lldb/bindings/interface/SBDebugger.i
index cf4411980cc30..aae72dd513940 100644
--- a/lldb/bindings/interface/SBDebugger.i
+++ b/lldb/bindings/interface/SBDebugger.i
@@ -479,6 +479,8 @@ public:
     lldb::SBTypeSynthetic
     GetSyntheticForType (lldb::SBTypeNameSpecifier);
 
+    SBStructuredData GetScriptInterpreterInfo(ScriptLanguage);
+
     STRING_EXTENSION(SBDebugger)
 
     %feature("docstring",

diff  --git a/lldb/bindings/python/CMakeLists.txt b/lldb/bindings/python/CMakeLists.txt
index 9422ee00cb5fc..5aabaf574636c 100644
--- a/lldb/bindings/python/CMakeLists.txt
+++ b/lldb/bindings/python/CMakeLists.txt
@@ -23,6 +23,18 @@ add_custom_target(swig_wrapper_python ALL DEPENDS
   ${CMAKE_CURRENT_BINARY_DIR}/lldb.py
 )
 
+if (NOT WIN32)
+add_custom_command(
+  OUTPUT  ${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb-python
+  VERBATIM
+  COMMAND ${CMAKE_COMMAND} -E copy lldb-python ${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb-python
+  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+)
+add_custom_target(lldb-python-wrapper ALL DEPENDS
+  ${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb-python
+)
+endif()
+
 function(create_python_package swig_target working_dir pkg_dir)
   cmake_parse_arguments(ARG "NOINIT" "" "FILES" ${ARGN})
   if(ARG_FILES)
@@ -149,6 +161,11 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
   create_relative_symlink(${swig_target} ${LIBLLDB_SYMLINK_DEST}
                           ${lldb_python_target_dir} ${LIBLLDB_SYMLINK_OUTPUT_FILE})
 
+
+  if (NOT WIN32)
+  add_dependencies(${swig_target} lldb-python-wrapper)
+  endif()
+
   if(NOT LLDB_BUILD_FRAMEWORK)
     set(LLDB_ARGDUMPER_FILENAME "lldb-argdumper${CMAKE_EXECUTABLE_SUFFIX}")
     create_relative_symlink(${swig_target} "${LLVM_RUNTIME_OUTPUT_INTDIR}/${LLDB_ARGDUMPER_FILENAME}"

diff  --git a/lldb/bindings/python/lldb-python b/lldb/bindings/python/lldb-python
new file mode 100755
index 0000000000000..3bb3b332d8523
--- /dev/null
+++ b/lldb/bindings/python/lldb-python
@@ -0,0 +1,17 @@
+#!/usr/bin/env python3
+
+import subprocess
+import os
+import sys
+import json
+
+lldb = os.path.join(os.path.dirname(__file__), 'lldb')
+
+info_json = subprocess.run([lldb, "-l", "python", "-print-script-interpreter-info"],
+    check=True, stdout=subprocess.PIPE, encoding='utf8').stdout
+info = json.loads(info_json)
+
+os.environ["PYTHONPATH"] = (
+    info["lldb-pythonpath"] + os.path.pathsep + os.environ.get("PYTHONPATH", ""))
+
+os.execl(info["executable"], info["executable"], *sys.argv[1:])

diff  --git a/lldb/docs/man/lldb.rst b/lldb/docs/man/lldb.rst
index 35db1dc68c129..10b143cd0de8f 100644
--- a/lldb/docs/man/lldb.rst
+++ b/lldb/docs/man/lldb.rst
@@ -234,6 +234,10 @@ SCRIPTING
 
  Alias for --script-language
 
+.. option:: --print-script-interpreter-info
+
+  Prints out a json dictionary with information about the scripting language interpreter.
+
 .. option:: --python-path
 
  Prints out the path to the lldb.py file for this version of lldb.

diff  --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h
index ef62141f579d6..64081f79205d6 100644
--- a/lldb/include/lldb/API/SBDebugger.h
+++ b/lldb/include/lldb/API/SBDebugger.h
@@ -247,6 +247,8 @@ class LLDB_API SBDebugger {
 
   lldb::ScriptLanguage GetScriptingLanguage(const char *script_language_name);
 
+  SBStructuredData GetScriptInterpreterInfo(ScriptLanguage);
+
   static const char *GetVersionString();
 
   static const char *StateAsCString(lldb::StateType state);

diff  --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
index 56cfb5cb8dd54..2b96021fffc99 100644
--- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h
+++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h
@@ -148,6 +148,8 @@ class ScriptInterpreter : public PluginInterface {
       lldb::ScriptedProcessInterfaceUP scripted_process_interface_up =
           std::make_unique<ScriptedProcessInterface>());
 
+  virtual StructuredData::DictionarySP GetInterpreterInfo();
+
   ~ScriptInterpreter() override = default;
 
   virtual bool Interrupt() { return false; }

diff  --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp
index c22662d764a7f..4bb23c3e705c6 100644
--- a/lldb/source/API/SBDebugger.cpp
+++ b/lldb/source/API/SBDebugger.cpp
@@ -680,6 +680,21 @@ SBDebugger::GetScriptingLanguage(const char *script_language_name) {
       llvm::StringRef(script_language_name), eScriptLanguageDefault, nullptr);
 }
 
+SBStructuredData
+SBDebugger::GetScriptInterpreterInfo(lldb::ScriptLanguage language) {
+  LLDB_RECORD_METHOD(SBStructuredData, SBDebugger, GetScriptInterpreterInfo,
+                     (lldb::ScriptLanguage), language);
+  SBStructuredData data;
+  if (m_opaque_sp) {
+    lldb_private::ScriptInterpreter *interp =
+        m_opaque_sp->GetScriptInterpreter(language);
+    if (interp) {
+      data.m_impl_up->SetObjectSP(interp->GetInterpreterInfo());
+    }
+  }
+  return LLDB_RECORD_RESULT(data);
+}
+
 const char *SBDebugger::GetVersionString() {
   LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBDebugger, GetVersionString);
 
@@ -1783,6 +1798,8 @@ template <> void RegisterMethods<SBDebugger>(Registry &R) {
                               (const char *));
   LLDB_REGISTER_METHOD(lldb::ScriptLanguage, SBDebugger, GetScriptingLanguage,
                        (const char *));
+  LLDB_REGISTER_METHOD(SBStructuredData, SBDebugger, GetScriptInterpreterInfo,
+                       (lldb::ScriptLanguage));
   LLDB_REGISTER_STATIC_METHOD(const char *, SBDebugger, GetVersionString, ());
   LLDB_REGISTER_STATIC_METHOD(const char *, SBDebugger, StateAsCString,
                               (lldb::StateType));

diff  --git a/lldb/source/Interpreter/ScriptInterpreter.cpp b/lldb/source/Interpreter/ScriptInterpreter.cpp
index 53e57ad59893d..fbdcbb8da8685 100644
--- a/lldb/source/Interpreter/ScriptInterpreter.cpp
+++ b/lldb/source/Interpreter/ScriptInterpreter.cpp
@@ -46,6 +46,10 @@ void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
       "This script interpreter does not support watchpoint callbacks.");
 }
 
+StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {
+  return nullptr;
+}
+
 bool ScriptInterpreter::LoadScriptingModule(const char *filename,
                                             const LoadScriptOptions &options,
                                             lldb_private::Status &error,

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
index 7c821e5933823..c677abfaa5f20 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp
@@ -148,6 +148,12 @@ ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger)
 
 ScriptInterpreterLua::~ScriptInterpreterLua() = default;
 
+StructuredData::DictionarySP ScriptInterpreterLua::GetInterpreterInfo() {
+  auto info = std::make_shared<StructuredData::Dictionary>();
+  info->AddStringItem("language", "lua");
+  return info;
+}
+
 bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command,
                                           CommandReturnObject *result,
                                           const ExecuteScriptOptions &options) {

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
index f3ca1e6b257d9..b601779ff301c 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h
@@ -49,6 +49,8 @@ class ScriptInterpreterLua : public ScriptInterpreter {
                            StructuredData::ObjectSP *module_sp = nullptr,
                            FileSpec extra_search_dir = {}) override;
 
+  StructuredData::DictionarySP GetInterpreterInfo() override;
+
   // Static Functions
   static void Initialize();
 

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
index 9b7508239f1b4..7c71c9329e579 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp
@@ -998,20 +998,6 @@ bool PythonFile::Check(PyObject *py_obj) {
 #endif
 }
 
-namespace {
-class GIL {
-public:
-  GIL() {
-    m_state = PyGILState_Ensure();
-    assert(!PyErr_Occurred());
-  }
-  ~GIL() { PyGILState_Release(m_state); }
-
-protected:
-  PyGILState_STATE m_state;
-};
-} // namespace
-
 const char *PythonException::toCString() const {
   if (!m_repr_bytes)
     return "unknown exception";

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
index 4577253227cd4..56bc55d239d12 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h
@@ -71,6 +71,18 @@ class PythonDictionary;
 class PythonInteger;
 class PythonException;
 
+class GIL {
+public:
+  GIL() {
+    m_state = PyGILState_Ensure();
+    assert(!PyErr_Occurred());
+  }
+  ~GIL() { PyGILState_Release(m_state); }
+
+protected:
+  PyGILState_STATE m_state;
+};
+
 class StructuredPythonObject : public StructuredData::Generic {
 public:
   StructuredPythonObject() : StructuredData::Generic() {}

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
index 74fb42ae03308..418e2239a771e 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
@@ -410,6 +410,35 @@ FileSpec ScriptInterpreterPython::GetPythonDir() {
   return g_spec;
 }
 
+StructuredData::DictionarySP ScriptInterpreterPython::GetInterpreterInfo() {
+  GIL gil;
+  FileSpec python_dir_spec = GetPythonDir();
+  if (!python_dir_spec)
+    return nullptr;
+  PythonString python_dir(python_dir_spec.GetPath());
+  PythonDictionary info(PyInitialValue::Empty);
+  llvm::Error error = info.SetItem("lldb-pythonpath", python_dir);
+  if (error)
+    return nullptr;
+  static const char script[] = R"(
+def main(info):
+  import sys
+  import os
+  name = 'python' + str(sys.version_info.major)
+  info.update({
+    "language": "python",
+    "prefix": sys.prefix,
+    "executable": os.path.join(sys.prefix, "bin", name),
+  })
+  return info
+)";
+  PythonScript get_info(script);
+  auto info_json = unwrapIgnoringErrors(As<PythonDictionary>(get_info(info)));
+  if (!info_json)
+    return nullptr;
+  return info_json.CreateStructuredDictionary();
+}
+
 void ScriptInterpreterPython::SharedLibraryDirectoryHelper(
     FileSpec &this_file) {
   // When we're loaded from python, this_file will point to the file inside the

diff  --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
index 83cc15da56c94..8cfc24e71283a 100644
--- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
+++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h
@@ -46,6 +46,7 @@ class ScriptInterpreterPython : public ScriptInterpreter,
       : ScriptInterpreter(debugger, lldb::eScriptLanguagePython),
         IOHandlerDelegateMultiline("DONE") {}
 
+  StructuredData::DictionarySP GetInterpreterInfo() override;
   static void Initialize();
   static void Terminate();
   static llvm::StringRef GetPluginNameStatic() { return "script-python"; }

diff  --git a/lldb/test/API/functionalities/paths/TestPaths.py b/lldb/test/API/functionalities/paths/TestPaths.py
index 8ef20c54974b6..da26da28a562e 100644
--- a/lldb/test/API/functionalities/paths/TestPaths.py
+++ b/lldb/test/API/functionalities/paths/TestPaths.py
@@ -5,6 +5,8 @@
 
 import lldb
 import os
+import sys
+import json
 from lldbsuite.test.decorators import *
 from lldbsuite.test.lldbtest import *
 from lldbsuite.test import lldbutil
@@ -42,6 +44,21 @@ def test_paths(self):
         self.assertTrue(any([os.path.exists(os.path.join(shlib_dir, f)) for f in
             filenames]), "shlib_dir = " + shlib_dir)
 
+    @no_debug_info_test
+    def test_interpreter_info(self):
+        info_sd = self.dbg.GetScriptInterpreterInfo(self.dbg.GetScriptingLanguage("python"))
+        self.assertTrue(info_sd.IsValid())
+        stream = lldb.SBStream()
+        self.assertTrue(info_sd.GetAsJSON(stream).Success())
+        info = json.loads(stream.GetData())
+        prefix = info['prefix']
+        self.assertEqual(os.path.realpath(sys.prefix), os.path.realpath(prefix))
+        self.assertEqual(
+            os.path.realpath(os.path.join(info['lldb-pythonpath'], 'lldb')),
+            os.path.realpath(os.path.dirname(lldb.__file__)))
+        self.assertTrue(os.path.exists(info['executable']))
+        self.assertEqual(info['language'], 'python')
+
 
     @no_debug_info_test
     def test_directory_doesnt_end_with_slash(self):

diff  --git a/lldb/test/Shell/Driver/TestHelp.test b/lldb/test/Shell/Driver/TestHelp.test
index fc53e80bdc4d5..8ce6097cd7410 100644
--- a/lldb/test/Shell/Driver/TestHelp.test
+++ b/lldb/test/Shell/Driver/TestHelp.test
@@ -62,6 +62,7 @@ CHECK: -r
 
 CHECK: SCRIPTING
 CHECK: -l
+CHECK: --print-script-interpreter-info
 CHECK: --python-path
 CHECK: -P
 CHECK: --script-language

diff  --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp
index df070ebe4db8d..a51c124f96158 100644
--- a/lldb/tools/driver/Driver.cpp
+++ b/lldb/tools/driver/Driver.cpp
@@ -18,6 +18,7 @@
 #include "lldb/API/SBReproducer.h"
 #include "lldb/API/SBStream.h"
 #include "lldb/API/SBStringList.h"
+#include "lldb/API/SBStructuredData.h"
 
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Format.h"
@@ -201,6 +202,9 @@ SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
   if (args.hasArg(OPT_python_path)) {
     m_option_data.m_print_python_path = true;
   }
+  if (args.hasArg(OPT_print_script_interpreter_info)) {
+    m_option_data.m_print_script_interpreter_info = true;
+  }
 
   if (args.hasArg(OPT_batch)) {
     m_option_data.m_batch = true;
@@ -398,6 +402,22 @@ SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
     return error;
   }
 
+  if (m_option_data.m_print_script_interpreter_info) {
+    SBStructuredData info =
+        m_debugger.GetScriptInterpreterInfo(m_debugger.GetScriptLanguage());
+    if (!info) {
+      error.SetErrorString("no script interpreter.");
+    } else {
+      SBStream stream;
+      error = info.GetAsJSON(stream);
+      if (error.Success()) {
+        llvm::outs() << stream.GetData() << '\n';
+      }
+    }
+    exiting = true;
+    return error;
+  }
+
   return error;
 }
 

diff  --git a/lldb/tools/driver/Driver.h b/lldb/tools/driver/Driver.h
index 2d91491a25400..d5779b3c2c91b 100644
--- a/lldb/tools/driver/Driver.h
+++ b/lldb/tools/driver/Driver.h
@@ -79,6 +79,7 @@ class Driver : public lldb::SBBroadcaster {
     bool m_source_quietly = false;
     bool m_print_version = false;
     bool m_print_python_path = false;
+    bool m_print_script_interpreter_info = false;
     bool m_wait_for = false;
     bool m_repl = false;
     bool m_batch = false;

diff  --git a/lldb/tools/driver/Options.td b/lldb/tools/driver/Options.td
index be2b4bfa30044..d59ac314d5944 100644
--- a/lldb/tools/driver/Options.td
+++ b/lldb/tools/driver/Options.td
@@ -48,6 +48,10 @@ def: Flag<["-"], "P">,
   HelpText<"Alias for --python-path">,
   Group<grp_scripting>;
 
+def print_script_interpreter_info: F<"print-script-interpreter-info">,
+  HelpText<"Prints out a json dictionary with information about the scripting language interpreter.">,
+  Group<grp_scripting>;
+
 def script_language: Separate<["--", "-"], "script-language">,
   MetaVarName<"<language>">,
   HelpText<"Tells the debugger to use the specified scripting language for user-defined scripts.">,


        


More information about the lldb-commits mailing list