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.">, _______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits