Skip to content

Commit bbef51e

Browse files
[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
1 parent a8abd19 commit bbef51e

File tree

19 files changed

+158
-14
lines changed

19 files changed

+158
-14
lines changed

lldb/bindings/interface/SBDebugger.i

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,8 @@ public:
479479
lldb::SBTypeSynthetic
480480
GetSyntheticForType (lldb::SBTypeNameSpecifier);
481481

482+
SBStructuredData GetScriptInterpreterInfo(ScriptLanguage);
483+
482484
STRING_EXTENSION(SBDebugger)
483485

484486
%feature("docstring",

lldb/bindings/python/CMakeLists.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ add_custom_target(swig_wrapper_python ALL DEPENDS
2323
${CMAKE_CURRENT_BINARY_DIR}/lldb.py
2424
)
2525

26+
if (NOT WIN32)
27+
add_custom_command(
28+
OUTPUT ${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb-python
29+
VERBATIM
30+
COMMAND ${CMAKE_COMMAND} -E copy lldb-python ${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb-python
31+
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
32+
)
33+
add_custom_target(lldb-python-wrapper ALL DEPENDS
34+
${LLVM_RUNTIME_OUTPUT_INTDIR}/lldb-python
35+
)
36+
endif()
37+
2638
function(create_python_package swig_target working_dir pkg_dir)
2739
cmake_parse_arguments(ARG "NOINIT" "" "FILES" ${ARGN})
2840
if(ARG_FILES)
@@ -149,6 +161,11 @@ function(finish_swig_python swig_target lldb_python_bindings_dir lldb_python_tar
149161
create_relative_symlink(${swig_target} ${LIBLLDB_SYMLINK_DEST}
150162
${lldb_python_target_dir} ${LIBLLDB_SYMLINK_OUTPUT_FILE})
151163

164+
165+
if (NOT WIN32)
166+
add_dependencies(${swig_target} lldb-python-wrapper)
167+
endif()
168+
152169
if(NOT LLDB_BUILD_FRAMEWORK)
153170
set(LLDB_ARGDUMPER_FILENAME "lldb-argdumper${CMAKE_EXECUTABLE_SUFFIX}")
154171
create_relative_symlink(${swig_target} "${LLVM_RUNTIME_OUTPUT_INTDIR}/${LLDB_ARGDUMPER_FILENAME}"

lldb/bindings/python/lldb-python

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env python3
2+
3+
import subprocess
4+
import os
5+
import sys
6+
import json
7+
8+
lldb = os.path.join(os.path.dirname(__file__), 'lldb')
9+
10+
info_json = subprocess.run([lldb, "-l", "python", "-print-script-interpreter-info"],
11+
check=True, stdout=subprocess.PIPE, encoding='utf8').stdout
12+
info = json.loads(info_json)
13+
14+
os.environ["PYTHONPATH"] = (
15+
info["lldb-pythonpath"] + os.path.pathsep + os.environ.get("PYTHONPATH", ""))
16+
17+
os.execl(info["executable"], info["executable"], *sys.argv[1:])

lldb/docs/man/lldb.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,10 @@ SCRIPTING
234234

235235
Alias for --script-language
236236

237+
.. option:: --print-script-interpreter-info
238+
239+
Prints out a json dictionary with information about the scripting language interpreter.
240+
237241
.. option:: --python-path
238242

239243
Prints out the path to the lldb.py file for this version of lldb.

lldb/include/lldb/API/SBDebugger.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ class LLDB_API SBDebugger {
247247

248248
lldb::ScriptLanguage GetScriptingLanguage(const char *script_language_name);
249249

250+
SBStructuredData GetScriptInterpreterInfo(ScriptLanguage);
251+
250252
static const char *GetVersionString();
251253

252254
static const char *StateAsCString(lldb::StateType state);

lldb/include/lldb/Interpreter/ScriptInterpreter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ class ScriptInterpreter : public PluginInterface {
148148
lldb::ScriptedProcessInterfaceUP scripted_process_interface_up =
149149
std::make_unique<ScriptedProcessInterface>());
150150

151+
virtual StructuredData::DictionarySP GetInterpreterInfo();
152+
151153
~ScriptInterpreter() override = default;
152154

153155
virtual bool Interrupt() { return false; }

lldb/source/API/SBDebugger.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,21 @@ SBDebugger::GetScriptingLanguage(const char *script_language_name) {
680680
llvm::StringRef(script_language_name), eScriptLanguageDefault, nullptr);
681681
}
682682

683+
SBStructuredData
684+
SBDebugger::GetScriptInterpreterInfo(lldb::ScriptLanguage language) {
685+
LLDB_RECORD_METHOD(SBStructuredData, SBDebugger, GetScriptInterpreterInfo,
686+
(lldb::ScriptLanguage), language);
687+
SBStructuredData data;
688+
if (m_opaque_sp) {
689+
lldb_private::ScriptInterpreter *interp =
690+
m_opaque_sp->GetScriptInterpreter(language);
691+
if (interp) {
692+
data.m_impl_up->SetObjectSP(interp->GetInterpreterInfo());
693+
}
694+
}
695+
return LLDB_RECORD_RESULT(data);
696+
}
697+
683698
const char *SBDebugger::GetVersionString() {
684699
LLDB_RECORD_STATIC_METHOD_NO_ARGS(const char *, SBDebugger, GetVersionString);
685700

@@ -1783,6 +1798,8 @@ template <> void RegisterMethods<SBDebugger>(Registry &R) {
17831798
(const char *));
17841799
LLDB_REGISTER_METHOD(lldb::ScriptLanguage, SBDebugger, GetScriptingLanguage,
17851800
(const char *));
1801+
LLDB_REGISTER_METHOD(SBStructuredData, SBDebugger, GetScriptInterpreterInfo,
1802+
(lldb::ScriptLanguage));
17861803
LLDB_REGISTER_STATIC_METHOD(const char *, SBDebugger, GetVersionString, ());
17871804
LLDB_REGISTER_STATIC_METHOD(const char *, SBDebugger, StateAsCString,
17881805
(lldb::StateType));

lldb/source/Interpreter/ScriptInterpreter.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ void ScriptInterpreter::CollectDataForWatchpointCommandCallback(
4646
"This script interpreter does not support watchpoint callbacks.");
4747
}
4848

49+
StructuredData::DictionarySP ScriptInterpreter::GetInterpreterInfo() {
50+
return nullptr;
51+
}
52+
4953
bool ScriptInterpreter::LoadScriptingModule(const char *filename,
5054
const LoadScriptOptions &options,
5155
lldb_private::Status &error,

lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,12 @@ ScriptInterpreterLua::ScriptInterpreterLua(Debugger &debugger)
148148

149149
ScriptInterpreterLua::~ScriptInterpreterLua() = default;
150150

151+
StructuredData::DictionarySP ScriptInterpreterLua::GetInterpreterInfo() {
152+
auto info = std::make_shared<StructuredData::Dictionary>();
153+
info->AddStringItem("language", "lua");
154+
return info;
155+
}
156+
151157
bool ScriptInterpreterLua::ExecuteOneLine(llvm::StringRef command,
152158
CommandReturnObject *result,
153159
const ExecuteScriptOptions &options) {

lldb/source/Plugins/ScriptInterpreter/Lua/ScriptInterpreterLua.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ class ScriptInterpreterLua : public ScriptInterpreter {
4949
StructuredData::ObjectSP *module_sp = nullptr,
5050
FileSpec extra_search_dir = {}) override;
5151

52+
StructuredData::DictionarySP GetInterpreterInfo() override;
53+
5254
// Static Functions
5355
static void Initialize();
5456

lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -998,20 +998,6 @@ bool PythonFile::Check(PyObject *py_obj) {
998998
#endif
999999
}
10001000

1001-
namespace {
1002-
class GIL {
1003-
public:
1004-
GIL() {
1005-
m_state = PyGILState_Ensure();
1006-
assert(!PyErr_Occurred());
1007-
}
1008-
~GIL() { PyGILState_Release(m_state); }
1009-
1010-
protected:
1011-
PyGILState_STATE m_state;
1012-
};
1013-
} // namespace
1014-
10151001
const char *PythonException::toCString() const {
10161002
if (!m_repr_bytes)
10171003
return "unknown exception";

lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ class PythonDictionary;
7171
class PythonInteger;
7272
class PythonException;
7373

74+
class GIL {
75+
public:
76+
GIL() {
77+
m_state = PyGILState_Ensure();
78+
assert(!PyErr_Occurred());
79+
}
80+
~GIL() { PyGILState_Release(m_state); }
81+
82+
protected:
83+
PyGILState_STATE m_state;
84+
};
85+
7486
class StructuredPythonObject : public StructuredData::Generic {
7587
public:
7688
StructuredPythonObject() : StructuredData::Generic() {}

lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,35 @@ FileSpec ScriptInterpreterPython::GetPythonDir() {
410410
return g_spec;
411411
}
412412

413+
StructuredData::DictionarySP ScriptInterpreterPython::GetInterpreterInfo() {
414+
GIL gil;
415+
FileSpec python_dir_spec = GetPythonDir();
416+
if (!python_dir_spec)
417+
return nullptr;
418+
PythonString python_dir(python_dir_spec.GetPath());
419+
PythonDictionary info(PyInitialValue::Empty);
420+
llvm::Error error = info.SetItem("lldb-pythonpath", python_dir);
421+
if (error)
422+
return nullptr;
423+
static const char script[] = R"(
424+
def main(info):
425+
import sys
426+
import os
427+
name = 'python' + str(sys.version_info.major)
428+
info.update({
429+
"language": "python",
430+
"prefix": sys.prefix,
431+
"executable": os.path.join(sys.prefix, "bin", name),
432+
})
433+
return info
434+
)";
435+
PythonScript get_info(script);
436+
auto info_json = unwrapIgnoringErrors(As<PythonDictionary>(get_info(info)));
437+
if (!info_json)
438+
return nullptr;
439+
return info_json.CreateStructuredDictionary();
440+
}
441+
413442
void ScriptInterpreterPython::SharedLibraryDirectoryHelper(
414443
FileSpec &this_file) {
415444
// When we're loaded from python, this_file will point to the file inside the

lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class ScriptInterpreterPython : public ScriptInterpreter,
4646
: ScriptInterpreter(debugger, lldb::eScriptLanguagePython),
4747
IOHandlerDelegateMultiline("DONE") {}
4848

49+
StructuredData::DictionarySP GetInterpreterInfo() override;
4950
static void Initialize();
5051
static void Terminate();
5152
static llvm::StringRef GetPluginNameStatic() { return "script-python"; }

lldb/test/API/functionalities/paths/TestPaths.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
import lldb
77
import os
8+
import sys
9+
import json
810
from lldbsuite.test.decorators import *
911
from lldbsuite.test.lldbtest import *
1012
from lldbsuite.test import lldbutil
@@ -42,6 +44,21 @@ def test_paths(self):
4244
self.assertTrue(any([os.path.exists(os.path.join(shlib_dir, f)) for f in
4345
filenames]), "shlib_dir = " + shlib_dir)
4446

47+
@no_debug_info_test
48+
def test_interpreter_info(self):
49+
info_sd = self.dbg.GetScriptInterpreterInfo(self.dbg.GetScriptingLanguage("python"))
50+
self.assertTrue(info_sd.IsValid())
51+
stream = lldb.SBStream()
52+
self.assertTrue(info_sd.GetAsJSON(stream).Success())
53+
info = json.loads(stream.GetData())
54+
prefix = info['prefix']
55+
self.assertEqual(os.path.realpath(sys.prefix), os.path.realpath(prefix))
56+
self.assertEqual(
57+
os.path.realpath(os.path.join(info['lldb-pythonpath'], 'lldb')),
58+
os.path.realpath(os.path.dirname(lldb.__file__)))
59+
self.assertTrue(os.path.exists(info['executable']))
60+
self.assertEqual(info['language'], 'python')
61+
4562

4663
@no_debug_info_test
4764
def test_directory_doesnt_end_with_slash(self):

lldb/test/Shell/Driver/TestHelp.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ CHECK: -r
6262

6363
CHECK: SCRIPTING
6464
CHECK: -l
65+
CHECK: --print-script-interpreter-info
6566
CHECK: --python-path
6667
CHECK: -P
6768
CHECK: --script-language

lldb/tools/driver/Driver.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "lldb/API/SBReproducer.h"
1919
#include "lldb/API/SBStream.h"
2020
#include "lldb/API/SBStringList.h"
21+
#include "lldb/API/SBStructuredData.h"
2122

2223
#include "llvm/ADT/StringRef.h"
2324
#include "llvm/Support/Format.h"
@@ -201,6 +202,9 @@ SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
201202
if (args.hasArg(OPT_python_path)) {
202203
m_option_data.m_print_python_path = true;
203204
}
205+
if (args.hasArg(OPT_print_script_interpreter_info)) {
206+
m_option_data.m_print_script_interpreter_info = true;
207+
}
204208

205209
if (args.hasArg(OPT_batch)) {
206210
m_option_data.m_batch = true;
@@ -398,6 +402,22 @@ SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
398402
return error;
399403
}
400404

405+
if (m_option_data.m_print_script_interpreter_info) {
406+
SBStructuredData info =
407+
m_debugger.GetScriptInterpreterInfo(m_debugger.GetScriptLanguage());
408+
if (!info) {
409+
error.SetErrorString("no script interpreter.");
410+
} else {
411+
SBStream stream;
412+
error = info.GetAsJSON(stream);
413+
if (error.Success()) {
414+
llvm::outs() << stream.GetData() << '\n';
415+
}
416+
}
417+
exiting = true;
418+
return error;
419+
}
420+
401421
return error;
402422
}
403423

lldb/tools/driver/Driver.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ class Driver : public lldb::SBBroadcaster {
7979
bool m_source_quietly = false;
8080
bool m_print_version = false;
8181
bool m_print_python_path = false;
82+
bool m_print_script_interpreter_info = false;
8283
bool m_wait_for = false;
8384
bool m_repl = false;
8485
bool m_batch = false;

lldb/tools/driver/Options.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ def: Flag<["-"], "P">,
4848
HelpText<"Alias for --python-path">,
4949
Group<grp_scripting>;
5050

51+
def print_script_interpreter_info: F<"print-script-interpreter-info">,
52+
HelpText<"Prints out a json dictionary with information about the scripting language interpreter.">,
53+
Group<grp_scripting>;
54+
5155
def script_language: Separate<["--", "-"], "script-language">,
5256
MetaVarName<"<language>">,
5357
HelpText<"Tells the debugger to use the specified scripting language for user-defined scripts.">,

0 commit comments

Comments
 (0)