diff --git a/lldb/bindings/interface/SBDebugger.i b/lldb/bindings/interface/SBDebugger.i --- a/lldb/bindings/interface/SBDebugger.i +++ b/lldb/bindings/interface/SBDebugger.i @@ -479,6 +479,8 @@ 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 --- a/lldb/bindings/python/CMakeLists.txt +++ b/lldb/bindings/python/CMakeLists.txt @@ -23,6 +23,18 @@ ${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 @@ 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 --- /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 --- a/lldb/docs/man/lldb.rst +++ b/lldb/docs/man/lldb.rst @@ -234,6 +234,10 @@ 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 --- a/lldb/include/lldb/API/SBDebugger.h +++ b/lldb/include/lldb/API/SBDebugger.h @@ -247,6 +247,8 @@ 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 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -148,6 +148,8 @@ lldb::ScriptedProcessInterfaceUP scripted_process_interface_up = std::make_unique()); + 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 --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -680,6 +680,21 @@ 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 @@ (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 --- a/lldb/source/Interpreter/ScriptInterpreter.cpp +++ b/lldb/source/Interpreter/ScriptInterpreter.cpp @@ -46,6 +46,10 @@ "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.h b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h --- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h @@ -49,6 +49,8 @@ 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/Lua/ScriptInterpreterLua.cpp b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp @@ -148,6 +148,12 @@ ScriptInterpreterLua::~ScriptInterpreterLua() = default; +StructuredData::DictionarySP ScriptInterpreterLua::GetInterpreterInfo() { + auto info = std::make_shared(); + info->AddStringItem("language", "lua"); + return info; +} + bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command, CommandReturnObject *result, const ExecuteScriptOptions &options) { diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -71,6 +71,18 @@ 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/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -998,20 +998,6 @@ #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/ScriptInterpreterPython.h b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h @@ -46,6 +46,7 @@ : 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/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -410,6 +410,35 @@ 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(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/test/API/functionalities/paths/TestPaths.py b/lldb/test/API/functionalities/paths/TestPaths.py --- 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 @@ 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 --- a/lldb/test/Shell/Driver/TestHelp.test +++ b/lldb/test/Shell/Driver/TestHelp.test @@ -62,6 +62,7 @@ CHECK: SCRIPTING CHECK: -l +CHECK: --print-script-interpreter-info CHECK: --python-path CHECK: -P CHECK: --script-language diff --git a/lldb/tools/driver/Driver.h b/lldb/tools/driver/Driver.h --- a/lldb/tools/driver/Driver.h +++ b/lldb/tools/driver/Driver.h @@ -79,6 +79,7 @@ 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/Driver.cpp b/lldb/tools/driver/Driver.cpp --- 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 @@ 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 @@ 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/Options.td b/lldb/tools/driver/Options.td --- a/lldb/tools/driver/Options.td +++ b/lldb/tools/driver/Options.td @@ -48,6 +48,10 @@ HelpText<"Alias for --python-path">, Group; +def print_script_interpreter_info: F<"print-script-interpreter-info">, + HelpText<"Prints out a json dictionary with information about the scripting language interpreter.">, + Group; + def script_language: Separate<["--", "-"], "script-language">, MetaVarName<"">, HelpText<"Tells the debugger to use the specified scripting language for user-defined scripts.">,