lawrence_danna created this revision. lawrence_danna added reviewers: jasonmolenda, JDevlieghere, jingham. Herald added subscribers: dang, mgorny. lawrence_danna requested review of this revision. Herald added a project: LLDB.
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 lldb::PathType for SBHostOS::GetLLDBPath: ePathTypePythonPrefix is the path to python's sys.prefix - new command line option: lldb --python-prefix to print that prefix. - new tool (unix only): lldb-python which finds python and exec's it. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D112973 Files: lldb/bindings/python/CMakeLists.txt lldb/docs/man/lldb.rst lldb/docs/python_api_enums.rst lldb/include/lldb/lldb-enumerations.h lldb/source/API/SBHostOS.cpp 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
Index: lldb/tools/driver/Options.td =================================================================== --- lldb/tools/driver/Options.td +++ lldb/tools/driver/Options.td @@ -48,6 +48,10 @@ HelpText<"Alias for --python-path">, Group<grp_scripting>; +def python_prefix: F<"python-prefix">, + HelpText<"Prints out the sys.prefix for the python used by this lldb.">, + 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.">, Index: lldb/tools/driver/Driver.h =================================================================== --- lldb/tools/driver/Driver.h +++ 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_python_prefix = false; bool m_wait_for = false; bool m_repl = false; bool m_batch = false; Index: lldb/tools/driver/Driver.cpp =================================================================== --- lldb/tools/driver/Driver.cpp +++ lldb/tools/driver/Driver.cpp @@ -43,7 +43,7 @@ #include <cstring> #include <fcntl.h> -// Includes for pipe() +// Includes for pipe(), execv() #if defined(_WIN32) #include <fcntl.h> #include <io.h> @@ -201,6 +201,9 @@ if (args.hasArg(OPT_python_path)) { m_option_data.m_print_python_path = true; } + if (args.hasArg(OPT_python_prefix)) { + m_option_data.m_print_python_prefix = true; + } if (args.hasArg(OPT_batch)) { m_option_data.m_batch = true; @@ -398,6 +401,21 @@ return error; } + if (m_option_data.m_print_python_prefix) { + SBFileSpec python_file_spec = SBHostOS::GetLLDBPath(ePathTypePythonPrefix); + if (python_file_spec.IsValid()) { + char python_path[PATH_MAX]; + size_t num_chars = python_file_spec.GetPath(python_path, PATH_MAX); + if (num_chars < PATH_MAX) { + llvm::outs() << python_path << '\n'; + } else + llvm::outs() << "<PATH TOO LONG>\n"; + } else + llvm::outs() << "<COULD NOT FIND PATH>\n"; + exiting = true; + return error; + } + return error; } @@ -867,7 +885,49 @@ return llvm::None; } -int main(int argc, char const *argv[]) { +static bool should_exec_python(llvm::StringRef argv0) { +#if defined(_WIN32) + return false; +#else + return argv0 == "lldb-python" || argv0.endswith("/lldb-python"); +#endif +} + +static void exec_python(int argc, char *argv[]) { +#if !defined(_WIN32) + SBFileSpec pathspec = SBHostOS::GetLLDBPythonPath(); + if (!pathspec.IsValid()) { + llvm::errs() << "could not find python path.\n"; + exit(1); + } + std::string pythonpath = pathspec.GetDirectory(); + const char *oldpath = ::getenv("PYTHONPATH"); + if (oldpath) + pythonpath = pythonpath + ":" + oldpath; + ::setenv("PYTHONPATH", pythonpath.c_str(), true); + SBFileSpec spec = SBHostOS::GetLLDBPath(ePathTypePythonPrefix); + if (!spec.IsValid()) { + llvm::errs() << "could not find python prefix.\n"; + exit(1); + } + spec.AppendPathComponent("bin"); + spec.AppendPathComponent("python3"); + char buf[PATH_MAX]; + size_t num_chars = spec.GetPath(buf, PATH_MAX); + if (num_chars >= PATH_MAX) { + llvm::errs() << "path too long.\n"; + exit(1); + } + char *python_exe = buf; + argv[0] = python_exe; + ::execv(python_exe, argv); + llvm::errs() << "could not exec python.\n"; + exit(1); +#endif +} + +int main(int argc, char *argv[]) { + // Editline uses for example iswprint which is dependent on LC_CTYPE. std::setlocale(LC_ALL, ""); std::setlocale(LC_CTYPE, ""); @@ -877,17 +937,19 @@ llvm::InitLLVM IL(argc, argv, /*InstallPipeSignalExitHandler=*/false); // Parse arguments. - LLDBOptTable T; - unsigned MissingArgIndex; - unsigned MissingArgCount; - ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1); - opt::InputArgList input_args = - T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount); llvm::StringRef argv0 = llvm::sys::path::filename(argv[0]); - - if (input_args.hasArg(OPT_help)) { - printHelp(T, argv0); - return 0; + bool will_exec_python = should_exec_python(argv0); + unsigned MissingArgIndex = 0; + unsigned MissingArgCount = 0; + ArrayRef<const char *> arg_arr = makeArrayRef(argv + 1, argc - 1); + opt::InputArgList input_args; + if (!will_exec_python) { + LLDBOptTable T; + input_args = T.ParseArgs(arg_arr, MissingArgIndex, MissingArgCount); + if (input_args.hasArg(OPT_help)) { + printHelp(T, argv0); + return 0; + } } // Check for missing argument error. @@ -920,6 +982,9 @@ } SBHostOS::ThreadCreated("<lldb.driver.main-thread>"); + if (will_exec_python) + exec_python(argc, argv); + signal(SIGINT, sigint_handler); #if !defined(_MSC_VER) signal(SIGPIPE, SIG_IGN); Index: lldb/test/Shell/Driver/TestHelp.test =================================================================== --- lldb/test/Shell/Driver/TestHelp.test +++ lldb/test/Shell/Driver/TestHelp.test @@ -63,5 +63,6 @@ CHECK: SCRIPTING CHECK: -l CHECK: --python-path +CHECK: --python-prefix CHECK: -P CHECK: --script-language Index: lldb/test/API/functionalities/paths/TestPaths.py =================================================================== --- lldb/test/API/functionalities/paths/TestPaths.py +++ lldb/test/API/functionalities/paths/TestPaths.py @@ -5,6 +5,7 @@ import lldb import os +import sys from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil @@ -22,10 +23,12 @@ lldb.ePathTypeSupportExecutableDir, lldb.ePathTypeHeaderDir, lldb.ePathTypePythonDir, + lldb.ePathTypePythonPrefix, lldb.ePathTypeLLDBSystemPlugins, lldb.ePathTypeLLDBUserPlugins, lldb.ePathTypeLLDBTempSystemDir, - lldb.ePathTypeClangDir] + lldb.ePathTypeClangDir, + lldb.ePathTypePythonPrefix] for path_type in dir_path_types: f = lldb.SBHostOS.GetLLDBPath(path_type) @@ -42,6 +45,10 @@ 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_prefix(self): + prefix = lldb.SBHostOS.GetLLDBPath(lldb.ePathTypePythonPrefix).GetDirectory() + self.assertEqual(os.path.realpath(sys.prefix), os.path.realpath(prefix)) @no_debug_info_test def test_directory_doesnt_end_with_slash(self): Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h +++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h @@ -51,6 +51,7 @@ static llvm::StringRef GetPluginNameStatic() { return "script-python"; } static llvm::StringRef GetPluginDescriptionStatic(); static FileSpec GetPythonDir(); + static FileSpec GetPythonPrefix(); static void SharedLibraryDirectoryHelper(FileSpec &this_file); protected: Index: lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp =================================================================== --- lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -410,6 +410,20 @@ return g_spec; } +FileSpec ScriptInterpreterPython::GetPythonPrefix() { + FileSpec spec; + InitializePythonRAII initialize_guard; + wchar_t *pyhome = Py_GetPrefix(); + if (pyhome) { + char *pyhome8 = Py_EncodeLocale(pyhome, NULL); + if (pyhome8) { + spec.GetDirectory().SetString(pyhome8); + PyMem_Free(pyhome8); + } + } + return spec; +} + void ScriptInterpreterPython::SharedLibraryDirectoryHelper( FileSpec &this_file) { // When we're loaded from python, this_file will point to the file inside the Index: lldb/source/API/SBHostOS.cpp =================================================================== --- lldb/source/API/SBHostOS.cpp +++ lldb/source/API/SBHostOS.cpp @@ -63,6 +63,11 @@ case ePathTypePythonDir: #if LLDB_ENABLE_PYTHON fspec = ScriptInterpreterPython::GetPythonDir(); +#endif + break; + case ePathTypePythonPrefix: +#if LLDB_ENABLE_PYTHON + fspec = ScriptInterpreterPython::GetPythonPrefix(); #endif break; case ePathTypeLLDBSystemPlugins: Index: lldb/include/lldb/lldb-enumerations.h =================================================================== --- lldb/include/lldb/lldb-enumerations.h +++ lldb/include/lldb/lldb-enumerations.h @@ -1011,7 +1011,8 @@ ePathTypeGlobalLLDBTempSystemDir, ///< The LLDB temp directory for this ///< system, NOT cleaned up on a process ///< exit. - ePathTypeClangDir ///< Find path to Clang builtin headers + ePathTypeClangDir, ///< Find path to Clang builtin headers + ePathTypePythonPrefix, ///< Find python's sys.prefix path }; /// Kind of member function. Index: lldb/docs/python_api_enums.rst =================================================================== --- lldb/docs/python_api_enums.rst +++ lldb/docs/python_api_enums.rst @@ -1291,6 +1291,10 @@ Find Python modules (PYTHONPATH) directory. +.. py:data:: ePathTypePythonPrefix + + Find Python's ``sys.prefix`` path. + .. py:data:: ePathTypeLLDBSystemPlugins System plug-ins directory Index: lldb/docs/man/lldb.rst =================================================================== --- lldb/docs/man/lldb.rst +++ lldb/docs/man/lldb.rst @@ -238,6 +238,10 @@ Prints out the path to the lldb.py file for this version of lldb. +.. option:: --python-prefix + + Prints out the sys.prefix for the python used by this lldb. + .. option:: -P Alias for --python-path Index: lldb/bindings/python/CMakeLists.txt =================================================================== --- lldb/bindings/python/CMakeLists.txt +++ lldb/bindings/python/CMakeLists.txt @@ -149,6 +149,12 @@ create_relative_symlink(${swig_target} ${LIBLLDB_SYMLINK_DEST} ${lldb_python_target_dir} ${LIBLLDB_SYMLINK_OUTPUT_FILE}) + if (NOT WIN32) + add_custom_command(TARGET ${swig_target} POST_BUILD VERBATIM + COMMAND ${CMAKE_COMMAND} -E "create_symlink" "lldb" "lldb-python" + WORKING_DIRECTORY ${LLVM_RUNTIME_OUTPUT_INTDIR}) + 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}"
_______________________________________________ lldb-commits mailing list lldb-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits