mirror-llvm-project/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

2889 lines
105 KiB
C++
Raw Normal View History

//===-- ScriptInterpreterPython.cpp ---------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb-python.h"
#include "Interfaces/ScriptInterpreterPythonInterfaces.h"
#include "PythonDataObjects.h"
#include "PythonReadline.h"
#include "SWIGPythonBridge.h"
#include "ScriptInterpreterPythonImpl.h"
#include "lldb/API/SBError.h"
Add the ability to define a Python based command that uses CommandObjectParsed (#70734) This allows you to specify options and arguments and their definitions and then have lldb handle the completions, help, etc. in the same way that lldb does for its parsed commands internally. This feature has some design considerations as well as the code, so I've also set up an RFC, but I did this one first and will put the RFC address in here once I've pushed it... Note, the lldb "ParsedCommand interface" doesn't actually do all the work that it should. For instance, saying the type of an option that has a completer doesn't automatically hook up the completer, and ditto for argument values. We also do almost no work to verify that the arguments match their definition, or do auto-completion for them. This patch allows you to make a command that's bug-for-bug compatible with built-in ones, but I didn't want to stall it on getting the auto-command checking to work all the way correctly. As an overall design note, my primary goal here was to make an interface that worked well in the script language. For that I needed, for instance, to have a property-based way to get all the option values that were specified. It was much more convenient to do that by making a fairly bare-bones C interface to define the options and arguments of a command, and set their values, and then wrap that in a Python class (installed along with the other bits of the lldb python module) which you can then derive from to make your new command. This approach will also make it easier to experiment. See the file test_commands.py in the test case for examples of how this works.
2024-02-13 10:09:47 -09:00
#include "lldb/API/SBExecutionContext.h"
#include "lldb/API/SBFrame.h"
#include "lldb/API/SBValue.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Breakpoint/WatchpointOptions.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/PluginManager.h"
#include "lldb/Core/ThreadedCommunication.h"
#include "lldb/DataFormatters/TypeSummary.h"
#include "lldb/Host/Config.h"
#include "lldb/Host/ConnectionFileDescriptor.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/Pipe.h"
#include "lldb/Host/StreamFile.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Utility/Instrumentation.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/StructuredData.h"
#include "lldb/Utility/Timer.h"
#include "lldb/ValueObject/ValueObject.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/ErrorExtras.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormatAdapters.h"
#if defined(_WIN32)
#include "lldb/Host/windows/ConnectionGenericFileWindows.h"
#endif
#include <cstdio>
#include <cstdlib>
#include <memory>
#include <optional>
#include <stdlib.h>
#include <string>
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::python;
using llvm::Expected;
LLDB_PLUGIN_DEFINE(ScriptInterpreterPython)
// Defined in the SWIG source file
extern "C" PyObject *PyInit__lldb(void);
#define LLDBSwigPyInit PyInit__lldb
[lldb] Fix that the embedded Python REPL crashes if it receives SIGINT When LLDB receives a SIGINT while running the embedded Python REPL it currently just crashes in ScriptInterpreterPythonImpl::Interrupt with an error such as the one below: Fatal Python error: PyThreadState_Get: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) The faulty code that causes this error is this part of ScriptInterpreterPythonImpl::Interrupt: PyThreadState *state = PyThreadState_GET(); if (!state) state = GetThreadState(); if (state) { long tid = state->thread_id; PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); The obvious fix I tried is to just acquire the GIL before this code is running which fixes the crash but the KeyboardInterrupt we want to raise immediately is actually just queued and would only be raised once the next line of input has been parsed (which e.g. won't interrupt Python code that is currently waiting on a timer or IO from what I can see). Also none of the functions we call here is marked as safe to be called from a signal handler from what I can see, so we might still end up crashing here with some bad timing. Python 3.2 introduced PyErr_SetInterrupt to solve this and the function takes care of all the details and avoids doing anything that isn't safe to do inside a signal handler. The only thing we need to do is to manually setup our own fake SIGINT handler that behaves the same way as the standalone Python REPL signal handler (which raises a KeyboardInterrupt). From what I understand the old code used to work with Python 2 so I kept the old code around until we officially drop support for Python 2. There is a small gap here with Python 3.0->3.1 where we might still be crashing, but those versions have reached their EOL more than a decade ago so I think we don't need to bother about them. Differential revision: https://reviews.llvm.org/D104886
2022-01-13 10:26:20 -09:00
#if defined(_WIN32)
// Don't mess with the signal handlers on Windows.
#define LLDB_USE_PYTHON_SET_INTERRUPT 0
#else
#define LLDB_USE_PYTHON_SET_INTERRUPT 1
[lldb] Fix that the embedded Python REPL crashes if it receives SIGINT When LLDB receives a SIGINT while running the embedded Python REPL it currently just crashes in ScriptInterpreterPythonImpl::Interrupt with an error such as the one below: Fatal Python error: PyThreadState_Get: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) The faulty code that causes this error is this part of ScriptInterpreterPythonImpl::Interrupt: PyThreadState *state = PyThreadState_GET(); if (!state) state = GetThreadState(); if (state) { long tid = state->thread_id; PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); The obvious fix I tried is to just acquire the GIL before this code is running which fixes the crash but the KeyboardInterrupt we want to raise immediately is actually just queued and would only be raised once the next line of input has been parsed (which e.g. won't interrupt Python code that is currently waiting on a timer or IO from what I can see). Also none of the functions we call here is marked as safe to be called from a signal handler from what I can see, so we might still end up crashing here with some bad timing. Python 3.2 introduced PyErr_SetInterrupt to solve this and the function takes care of all the details and avoids doing anything that isn't safe to do inside a signal handler. The only thing we need to do is to manually setup our own fake SIGINT handler that behaves the same way as the standalone Python REPL signal handler (which raises a KeyboardInterrupt). From what I understand the old code used to work with Python 2 so I kept the old code around until we officially drop support for Python 2. There is a small gap here with Python 3.0->3.1 where we might still be crashing, but those versions have reached their EOL more than a decade ago so I think we don't need to bother about them. Differential revision: https://reviews.llvm.org/D104886
2022-01-13 10:26:20 -09:00
#endif
static ScriptInterpreterPythonImpl *GetPythonInterpreter(Debugger &debugger) {
ScriptInterpreter *script_interpreter =
debugger.GetScriptInterpreter(true, lldb::eScriptLanguagePython);
return static_cast<ScriptInterpreterPythonImpl *>(script_interpreter);
}
namespace {
// Initializing Python is not a straightforward process. We cannot control
// what external code may have done before getting to this point in LLDB,
// including potentially having already initialized Python, so we need to do a
// lot of work to ensure that the existing state of the system is maintained
// across our initialization. We do this by using an RAII pattern where we
// save off initial state at the beginning, and restore it at the end
struct InitializePythonRAII {
public:
InitializePythonRAII() {
// The table of built-in modules can only be extended before Python is
// initialized.
if (!Py_IsInitialized()) {
#ifdef LLDB_USE_LIBEDIT_READLINE_COMPAT_MODULE
// Python's readline is incompatible with libedit being linked into lldb.
// Provide a patched version local to the embedded interpreter.
PyImport_AppendInittab("readline", initlldb_readline);
#endif
// Register _lldb as a built-in module.
PyImport_AppendInittab("_lldb", LLDBSwigPyInit);
}
#if LLDB_EMBED_PYTHON_HOME
PyConfig config;
PyConfig_InitPythonConfig(&config);
static std::string g_python_home = []() -> std::string {
if (llvm::sys::path::is_absolute(LLDB_PYTHON_HOME))
return LLDB_PYTHON_HOME;
FileSpec spec = HostInfo::GetShlibDir();
if (!spec)
return {};
spec.AppendPathComponent(LLDB_PYTHON_HOME);
return spec.GetPath();
}();
if (!g_python_home.empty()) {
PyConfig_SetBytesString(&config, &config.home, g_python_home.c_str());
}
config.install_signal_handlers = 0;
Py_InitializeFromConfig(&config);
PyConfig_Clear(&config);
#else
Py_InitializeEx(/*install_sigs=*/0);
#endif
// The only case we should go further and acquire the GIL: it is unlocked.
PyGILState_STATE gil_state = PyGILState_Ensure();
if (gil_state != PyGILState_UNLOCKED)
return;
m_was_already_initialized = true;
m_gil_state = gil_state;
[lldb] Rename formatv verbose log call, misc log cleanups [NFC] (#186951) lldb had three preprocessor defines for logging, LLDB_LOG - formatv style argument LLDB_LOGF - printf style argument LLDB_LOGV - formatv style argument, only when verbose enabled If you weren't looking at Log.h and the definition of these three, and wanted to log something with formatv, it was easy to use LLDB_LOGV by accident. We just had a situation where an important log statement wasn't logging and it turned out to be this. This is fragile if you aren't looking at the header directly, so I'd like to make this more explicit. My proposal: LLDB_LOG - formatv style argument LLDB_LOG_VERBOSE - formatv style argument, only when verbose enabled LLDB_LOGF - printf style argument LLDB_LOGF_VERBOSE - printf style argument, only when verbose enabled The new fouth one is to remove several places where we do `if (log && log->GetVerbose()) LLDB_LOGF (...)` in the sources today, and make both styles consistent. This PR implements that change, mechanically changing all LLDB_LOGV's to LLDB_LOG_VERBOSE. It also updates many of the `if (log && log->GetVerbose()) LLDB_LOGF`'s. Some uses of this conditional expression do extra calculations in addition to logging, and so those were left as-is so we're not doing throwaway work when running without verbose logging. There were many instances throughout lldb where callers are still doing `if (log) LLDB_LOG*(...)`, a remnant of when all calls were to the `Log` object's `Printf()` method, and you had to check if your local Log* pointer was non-nullptr before calling the method. I removed those, again keeping ones where work for logging is done in the block of code. The code changes are all mechanical and uninteresting, but the question of whether this naming change is widely agreed on is maybe worth discussing.
2026-03-18 15:31:33 -08:00
LLDB_LOG_VERBOSE(
GetLog(LLDBLog::Script), "Ensured PyGILState. Previous state = {0}",
m_gil_state == PyGILState_UNLOCKED ? "unlocked" : "locked");
}
~InitializePythonRAII() {
if (m_was_already_initialized) {
[lldb] Rename formatv verbose log call, misc log cleanups [NFC] (#186951) lldb had three preprocessor defines for logging, LLDB_LOG - formatv style argument LLDB_LOGF - printf style argument LLDB_LOGV - formatv style argument, only when verbose enabled If you weren't looking at Log.h and the definition of these three, and wanted to log something with formatv, it was easy to use LLDB_LOGV by accident. We just had a situation where an important log statement wasn't logging and it turned out to be this. This is fragile if you aren't looking at the header directly, so I'd like to make this more explicit. My proposal: LLDB_LOG - formatv style argument LLDB_LOG_VERBOSE - formatv style argument, only when verbose enabled LLDB_LOGF - printf style argument LLDB_LOGF_VERBOSE - printf style argument, only when verbose enabled The new fouth one is to remove several places where we do `if (log && log->GetVerbose()) LLDB_LOGF (...)` in the sources today, and make both styles consistent. This PR implements that change, mechanically changing all LLDB_LOGV's to LLDB_LOG_VERBOSE. It also updates many of the `if (log && log->GetVerbose()) LLDB_LOGF`'s. Some uses of this conditional expression do extra calculations in addition to logging, and so those were left as-is so we're not doing throwaway work when running without verbose logging. There were many instances throughout lldb where callers are still doing `if (log) LLDB_LOG*(...)`, a remnant of when all calls were to the `Log` object's `Printf()` method, and you had to check if your local Log* pointer was non-nullptr before calling the method. I removed those, again keeping ones where work for logging is done in the block of code. The code changes are all mechanical and uninteresting, but the question of whether this naming change is widely agreed on is maybe worth discussing.
2026-03-18 15:31:33 -08:00
LLDB_LOG_VERBOSE(GetLog(LLDBLog::Script),
"Releasing PyGILState. Returning to state = {0}",
m_gil_state == PyGILState_UNLOCKED ? "unlocked"
: "locked");
PyGILState_Release(m_gil_state);
} else {
// We initialized the threads in this function, just unlock the GIL.
PyEval_SaveThread();
}
}
private:
PyGILState_STATE m_gil_state = PyGILState_UNLOCKED;
bool m_was_already_initialized = false;
};
#if LLDB_USE_PYTHON_SET_INTERRUPT
/// Saves the current signal handler for the specified signal and restores
/// it at the end of the current scope.
struct RestoreSignalHandlerScope {
/// The signal handler.
struct sigaction m_prev_handler;
int m_signal_code;
RestoreSignalHandlerScope(int signal_code) : m_signal_code(signal_code) {
// Initialize sigaction to their default state.
std::memset(&m_prev_handler, 0, sizeof(m_prev_handler));
// Don't install a new handler, just read back the old one.
struct sigaction *new_handler = nullptr;
int signal_err = ::sigaction(m_signal_code, new_handler, &m_prev_handler);
lldbassert(signal_err == 0 && "sigaction failed to read handler");
}
~RestoreSignalHandlerScope() {
int signal_err = ::sigaction(m_signal_code, &m_prev_handler, nullptr);
lldbassert(signal_err == 0 && "sigaction failed to restore old handler");
}
};
#endif
} // namespace
void ScriptInterpreterPython::ComputePythonDirForApple(
llvm::SmallVectorImpl<char> &path) {
auto style = llvm::sys::path::Style::posix;
llvm::StringRef path_ref(path.begin(), path.size());
auto rbegin = llvm::sys::path::rbegin(path_ref, style);
auto rend = llvm::sys::path::rend(path_ref);
auto framework = std::find(rbegin, rend, "LLDB.framework");
if (framework == rend) {
ComputePythonDir(path);
return;
}
path.resize(framework - rend);
llvm::sys::path::append(path, style, "LLDB.framework", "Resources", "Python");
}
void ScriptInterpreterPython::ComputePythonDir(
llvm::SmallVectorImpl<char> &path) {
// Build the path by backing out of the lib dir, then building with whatever
// the real python interpreter uses. (e.g. lib for most, lib64 on RHEL
// x86_64, or bin on Windows).
llvm::sys::path::remove_filename(path);
llvm::sys::path::append(path, LLDB_PYTHON_RELATIVE_LIBDIR);
#if defined(_WIN32)
[NFC] Improve FileSpec internal APIs and usage in preparation for adding caching of resolved/absolute. Resubmission of https://reviews.llvm.org/D130309 with the 2 patches that fixed the linux buildbot, and new windows fixes. The FileSpec APIs allow users to modify instance variables directly by getting a non const reference to the directory and filename instance variables. This makes it impossible to control all of the times the FileSpec object is modified so we can clear cached member variables like m_resolved and with an upcoming patch caching if the file is relative or absolute. This patch modifies the APIs of FileSpec so no one can modify the directory or filename instance variables directly by adding set accessors and by removing the get accessors that are non const. Many clients were using FileSpec::GetCString(...) which returned a unique C string from a ConstString'ified version of the result of GetPath() which returned a std::string. This caused many locations to use this convenient function incorrectly and could cause many strings to be added to the constant string pool that didn't need to. Most clients were converted to using FileSpec::GetPath().c_str() when possible. Other clients were modified to use the newly renamed version of this function which returns an actualy ConstString: ConstString FileSpec::GetPathAsConstString(bool denormalize = true) const; This avoids the issue where people were getting an already uniqued "const char *" that came from a ConstString only to put the "const char *" back into a "ConstString" object. By returning the ConstString instead of a "const char *" clients can be more efficient with the result. The patch: - Removes the non const GetDirectory() and GetFilename() get accessors - Adds set accessors to replace the above functions: SetDirectory() and SetFilename(). - Adds ClearDirectory() and ClearFilename() to replace usage of the FileSpec::GetDirectory().Clear()/FileSpec::GetFilename().Clear() call sites - Fixed all incorrect usage of FileSpec::GetCString() to use FileSpec::GetPath().c_str() where appropriate, and updated other call sites that wanted a ConstString to use the newly returned ConstString appropriately and efficiently. Differential Revision: https://reviews.llvm.org/D130549
2022-07-25 22:29:30 -08:00
// This will be injected directly through FileSpec.SetDirectory(),
// so we need to normalize manually.
std::replace(path.begin(), path.end(), '\\', '/');
#endif
}
FileSpec ScriptInterpreterPython::GetPythonDir() {
static FileSpec g_spec = []() {
FileSpec spec = HostInfo::GetShlibDir();
if (!spec)
return FileSpec();
llvm::SmallString<64> path;
spec.GetPath(path);
#if defined(__APPLE__)
ComputePythonDirForApple(path);
#else
ComputePythonDir(path);
#endif
[NFC] Improve FileSpec internal APIs and usage in preparation for adding caching of resolved/absolute. Resubmission of https://reviews.llvm.org/D130309 with the 2 patches that fixed the linux buildbot, and new windows fixes. The FileSpec APIs allow users to modify instance variables directly by getting a non const reference to the directory and filename instance variables. This makes it impossible to control all of the times the FileSpec object is modified so we can clear cached member variables like m_resolved and with an upcoming patch caching if the file is relative or absolute. This patch modifies the APIs of FileSpec so no one can modify the directory or filename instance variables directly by adding set accessors and by removing the get accessors that are non const. Many clients were using FileSpec::GetCString(...) which returned a unique C string from a ConstString'ified version of the result of GetPath() which returned a std::string. This caused many locations to use this convenient function incorrectly and could cause many strings to be added to the constant string pool that didn't need to. Most clients were converted to using FileSpec::GetPath().c_str() when possible. Other clients were modified to use the newly renamed version of this function which returns an actualy ConstString: ConstString FileSpec::GetPathAsConstString(bool denormalize = true) const; This avoids the issue where people were getting an already uniqued "const char *" that came from a ConstString only to put the "const char *" back into a "ConstString" object. By returning the ConstString instead of a "const char *" clients can be more efficient with the result. The patch: - Removes the non const GetDirectory() and GetFilename() get accessors - Adds set accessors to replace the above functions: SetDirectory() and SetFilename(). - Adds ClearDirectory() and ClearFilename() to replace usage of the FileSpec::GetDirectory().Clear()/FileSpec::GetFilename().Clear() call sites - Fixed all incorrect usage of FileSpec::GetCString() to use FileSpec::GetPath().c_str() where appropriate, and updated other call sites that wanted a ConstString to use the newly returned ConstString appropriately and efficiently. Differential Revision: https://reviews.llvm.org/D130549
2022-07-25 22:29:30 -08:00
spec.SetDirectory(path);
return spec;
}();
return g_spec;
}
static const char GetInterpreterInfoScript[] = R"(
import os
import sys
def main(lldb_python_dir, python_exe_relative_path):
info = {
"lldb-pythonpath": lldb_python_dir,
"language": "python",
"prefix": sys.prefix,
"executable": os.path.join(sys.prefix, python_exe_relative_path)
}
return info
)";
static const char python_exe_relative_path[] = LLDB_PYTHON_EXE_RELATIVE_PATH;
[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
2021-11-10 09:33:33 -09:00
StructuredData::DictionarySP ScriptInterpreterPython::GetInterpreterInfo() {
GIL gil;
FileSpec python_dir_spec = GetPythonDir();
if (!python_dir_spec)
return nullptr;
PythonScript get_info(GetInterpreterInfoScript);
auto info_json = unwrapIgnoringErrors(
As<PythonDictionary>(get_info(PythonString(python_dir_spec.GetPath()),
PythonString(python_exe_relative_path))));
[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
2021-11-10 09:33:33 -09:00
if (!info_json)
return nullptr;
return info_json.CreateStructuredDictionary();
}
llvm::Expected<std::string> ScriptInterpreterPython::ExtensionToImportPath(
lldb::ScriptedExtension extension) {
switch (extension) {
case eScriptedExtensionOperatingSystem:
return "lldb.plugins.operating_system";
case eScriptedExtensionScriptedPlatform:
return "lldb.plugins.scripted_platform";
case eScriptedExtensionScriptedProcess:
return "lldb.plugins.scripted_process";
case eScriptedExtensionScriptedHook:
return "lldb.plugins.scripted_hook";
case eScriptedExtensionScriptedBreakpointResolver:
return "lldb.plugins.scripted_breakpoint";
case eScriptedExtensionScriptedThreadPlan:
return "lldb.plugins.scripted_thread_plan";
case eScriptedExtensionScriptedFrameProvider:
return "lldb.plugins.scripted_frame_provider";
case eScriptedExtensionScriptedThread:
case eScriptedExtensionScriptedFrame:
return "lldb.plugins.scripted_process";
case eScriptedExtensionScriptedStackFrameRecognizer:
return "lldb.plugins.scripted_stackframe_recognizer";
case eScriptedExtensionScriptedCommand:
case eScriptedExtensionParsedCommand:
return "lldb.plugins.scripted_command";
case eScriptedExtensionScriptedStringSummary:
return "lldb.plugins.scripted_string_summary";
case eScriptedExtensionScriptedSyntheticChildren:
return "lldb.plugins.scripted_synthetic_children";
case eScriptedExtensionInvalid:
return llvm::createStringError("invalid extension name");
}
return llvm::createStringError("invalid extension name");
}
llvm::Expected<StructuredData::ObjectSP>
ScriptInterpreterPython::GetExtensionSchema(
const llvm::SmallVector<llvm::StringRef> &extension_path) {
lldb::ScriptedExtension extension =
ScriptInterpreter::StringToExtension(extension_path.back());
auto import_path_or_err = ExtensionToImportPath(extension);
if (!import_path_or_err)
return import_path_or_err.takeError();
StreamString command_stream;
// __import__(path, fromlist=['']) imports the submodule and returns it
// directly (rather than the top-level package), as a single expression --
// this keeps the whole call eval-able in one line while guaranteeing the
// module is imported first; referencing "<import_path>.<ClassName>"
// directly would only work if something else had already imported
// <import_path> as a side effect.
command_stream.Printf("lldb.embedded_interpreter.generate_extension_schema("
"__import__('%s', fromlist=['']).%s)",
import_path_or_err->c_str(),
ScriptInterpreter::ExtensionToString(extension).data());
// Use eScriptReturnTypeOpaqueObject: it transfers a real owned reference
// we can safely extract the string from. eScriptReturnTypeCharStrOrNone
// instead hands back a pointer to a temporary Python object's buffer
// that gets destroyed (and, for a freshly created string like this one,
// deallocated) as soon as ExecuteOneLineWithReturn returns -- reading
// it afterwards is a use-after-free.
void *result_obj = nullptr;
if (!ExecuteOneLineWithReturn(
command_stream.GetData(),
ScriptInterpreter::eScriptReturnTypeOpaqueObject, &result_obj,
ExecuteScriptOptions().SetEnableIO(false)))
return llvm::createStringError("invalid extension schema format");
// ExecuteOneLineWithReturn releases the GIL before returning, so touching
// the returned object (Str() below can execute arbitrary Python code) must
// re-acquire it first. py_result is scoped so its destructor (a DECREF)
// also runs before the GIL is released below, not after.
std::string schema_str;
{
PyGILState_STATE gil_state = PyGILState_Ensure();
{
PythonObject py_result(PyRefType::Owned,
static_cast<PyObject *>(result_obj));
if (py_result.IsAllocated() && py_result.get() != Py_None)
schema_str = py_result.Str().GetString().str();
}
PyGILState_Release(gil_state);
}
if (schema_str.empty())
return llvm::createStringError("empty extension schema");
return StructuredData::ParseJSON(schema_str);
}
llvm::Error ScriptInterpreterPython::ParseExtensionSchema(
Stream &s, llvm::StringRef output_script_prefix,
const llvm::SmallVector<llvm::StringRef> &extension_path,
bool generate_non_abstract_methods, std::set<std::string> &typing_imports) {
auto schema_or_err = GetExtensionSchema(extension_path);
if (!schema_or_err)
return schema_or_err.takeError();
StructuredData::ObjectSP schema = *schema_or_err;
if (!schema)
return llvm::createStringError("empty extension schema");
StructuredData::Dictionary *dict = schema->GetAsDictionary();
if (!dict)
return llvm::createStringError("extension schema is not a JSON object");
// Merge each class' typing imports into the caller-owned set so the
// final `from typing import ...` line covers every class we emit.
StructuredData::Array *schema_typing;
if (dict->GetValueForKeyAsArray("typing_imports", schema_typing))
schema_typing->ForEach([&](StructuredData::Object *entry) {
if (auto *str = entry->GetAsString())
typing_imports.insert(str->GetValue().str());
return true;
});
llvm::StringRef base_class, import_path;
if (!dict->GetValueForKeyAsString("class", base_class))
return llvm::createStringError(
llvm::formatv("extension schema dictionary is missing 'class' key")
.str());
if (!dict->GetValueForKeyAsString("module", import_path))
return llvm::createStringError(
llvm::formatv("extension schema dictionary is missing 'module' key")
.str());
// imports
s.Printf("from %s import %s\n", import_path.data(), base_class.data());
s.EOL();
// class definition
s.Printf("class %s%s(%s):\n", output_script_prefix.data(), base_class.data(),
base_class.data());
s.IndentMore();
// Class docstring: list the non-callable members the base class exposes
// so the user sees what's available without having to hop back to the
// base class definition.
bool has_body = false;
StructuredData::Array *attributes;
if (dict->GetValueForKeyAsArray("attributes", attributes) &&
attributes->GetSize()) {
s.Indent();
s.PutCString("\"\"\"\n");
s.Indent();
s.Printf("Attributes inherited from %s:\n", base_class.data());
for (size_t i = 0; i < attributes->GetSize(); i++) {
auto maybe_dict = attributes->GetItemAtIndexAsDictionary(i);
if (!maybe_dict)
continue;
StructuredData::Dictionary *attr_dict = *maybe_dict;
llvm::StringRef attr_name;
if (!attr_dict->GetValueForKeyAsString("name", attr_name))
continue;
llvm::StringRef attr_type;
bool has_type = attr_dict->GetValueForKeyAsString("type", attr_type);
s.Indent();
s.Printf("- %s", attr_name.data());
if (has_type)
s.Printf(": %s", attr_type.data());
s.EOL();
}
s.Indent();
s.PutCString("\"\"\"\n\n");
has_body = true;
}
// members
StructuredData::Array *members;
if (!dict->GetValueForKeyAsArray("members", members))
return llvm::createStringError("missing 'members' key in extension schema");
// If the base class doesn't mark anything `@abstractmethod`, the filter
// "only stub abstract methods" would leave the derived class empty --
// which isn't a useful starting point. Fall back to emitting every
// method in that case so the user has actual code to edit.
bool any_abstract = false;
for (size_t i = 0; i < members->GetSize(); i++) {
auto maybe_dict = members->GetItemAtIndexAsDictionary(i);
if (!maybe_dict)
continue;
bool is_abstract = false;
if ((*maybe_dict)->GetValueForKeyAsBoolean("is_abstract", is_abstract) &&
is_abstract) {
any_abstract = true;
break;
}
}
bool emit_all_methods = generate_non_abstract_methods || !any_abstract;
for (size_t i = 0; i < members->GetSize(); i++) {
auto maybe_dict = members->GetItemAtIndexAsDictionary(i);
if (!maybe_dict)
return llvm::createStringError(
llvm::formatv(
"member at index {0} in extension schema isn't a dictionary")
.str());
StructuredData::Dictionary *member_dict = *maybe_dict;
llvm::StringRef symbol, args;
if (!member_dict->GetValueForKeyAsString("name", symbol))
return llvm::createStringError(
llvm::formatv(
"member at index {0} in extension schema is missing 'name' key")
.str());
if (!member_dict->GetValueForKeyAsString("signature", args))
return llvm::createStringError(
llvm::formatv("member at index {0} in extension schema is missing "
"'signature' key")
.str());
bool is_abstract = false;
bool has_is_abstract =
member_dict->GetValueForKeyAsBoolean("is_abstract", is_abstract);
if (!emit_all_methods)
if (!has_is_abstract || !is_abstract)
continue;
s.Indent();
s.Printf("def %s%s:\n", symbol.data(), args.data());
s.IndentMore();
llvm::StringRef documentation;
if (member_dict->GetValueForKeyAsString("doc", documentation)) {
s.Indent();
s.PutCString("\"\"\"\n");
llvm::SmallVector<llvm::StringRef> lines;
documentation.split(lines, "\n");
for (llvm::StringRef line : lines) {
s.Indent();
s.PutCString(line);
s.EOL();
}
s.Indent();
s.PutCString("\"\"\"");
s.EOL();
}
if (symbol == "__init__") {
// The base class' constructor sets up attributes (e.g. self.target,
// self.process) that the inherited, non-overridden methods rely on.
// Forward the same arguments so that state is still initialized.
// Splitting the param list on `,` requires bracket-depth awareness
// because annotations like `Union[X, Y]` also contain commas.
llvm::StringRef params = args.trim("()");
std::vector<std::string> forwarded_args;
int depth = 0;
size_t start = 0;
auto flush = [&](size_t end) {
llvm::StringRef param = params.slice(start, end);
param = param.split(':').first.split('=').first.trim();
if (!param.empty() && param != "self")
forwarded_args.push_back(param.str());
};
for (size_t i = 0; i < params.size(); ++i) {
char c = params[i];
if (c == '[' || c == '(' || c == '{')
++depth;
else if (c == ']' || c == ')' || c == '}')
--depth;
else if (c == ',' && depth == 0) {
flush(i);
start = i + 1;
}
}
flush(params.size());
s.Indent();
s.Printf("super().__init__(%s)\n",
llvm::join(forwarded_args, ", ").c_str());
}
s.Indent();
s.PutCString("# TODO: Implement\n");
s.Indent();
s.PutCString("pass\n\n");
s.IndentLess();
has_body = true;
}
// A class with no body is a Python syntax error, so emit `pass` when the
// base class has nothing to stub out (no methods and no attributes to
// document).
if (!has_body) {
s.Indent();
s.PutCString("pass\n");
}
return llvm::Error::success();
}
llvm::Expected<FileSpec> ScriptInterpreterPython::GenerateExtensionTemplate(
const std::string &name, std::vector<ExtensionTemplateRequest> &extensions,
bool generate_non_abstract_methods, std::string output_file) {
// `ParseExtensionSchema` accumulates every `typing` generic it sees
// (`Optional`, `Union`, `List`, ...) into this set so we can emit a
// targeted `from typing import ...` line only for what's actually
// referenced. The Python schema does the detection so we don't have
// to re-scan strings here.
std::set<std::string> typing_imports;
StreamString bodies;
for (const ExtensionTemplateRequest &extension : extensions) {
if (llvm::Error err =
ParseExtensionSchema(bodies, name, extension.path,
generate_non_abstract_methods, typing_imports))
return std::move(err);
bodies.PutCString("\n\n");
}
StreamString generated_file_stream;
generated_file_stream.PutCString("import lldb\n");
if (!typing_imports.empty()) {
std::vector<std::string> sorted_imports(typing_imports.begin(),
typing_imports.end());
generated_file_stream.Format("from typing import {0}\n",
llvm::join(sorted_imports, ", "));
}
generated_file_stream.PutCString("\n");
generated_file_stream.PutCString(bodies.GetString());
FileSpec save_location;
if (output_file.empty()) {
// Sanitize the caller-supplied class prefix so it can't escape the
// temp directory (`../`, path separators, ...). Only keep ASCII
// alphanumerics; everything else collapses to `_`, and an all-junk
// name falls back to a fixed default.
std::string sanitized;
sanitized.reserve(name.size());
for (char c : name)
sanitized.push_back(llvm::isAlnum(c) ? static_cast<char>(llvm::toLower(c))
: '_');
if (sanitized.find_first_not_of('_') == std::string::npos)
sanitized = "extension";
const std::string file_name = "lldb_" + sanitized + "_extension.py";
save_location = HostInfo::GetGlobalTempDir();
FileSystem::Instance().Resolve(save_location);
save_location.AppendPathComponent(file_name);
} else {
save_location = FileSpec(output_file);
FileSystem::Instance().Resolve(save_location);
}
File::OpenOptions flags = File::eOpenOptionWriteOnly |
File::eOpenOptionCanCreate |
File::eOpenOptionTruncate;
auto opened_file = FileSystem::Instance().Open(save_location, flags);
if (!opened_file)
return opened_file.takeError();
FileUP file = std::move(opened_file.get());
size_t byte_size = generated_file_stream.GetSize();
Status error = file->Write(generated_file_stream.GetData(), byte_size);
if (error.Fail() || byte_size != generated_file_stream.GetSize())
return llvm::createStringError("Unable to write to destination file. Bytes "
"written do not match generated file size.");
return save_location;
}
2021-02-15 11:51:32 -09:00
void ScriptInterpreterPython::SharedLibraryDirectoryHelper(
FileSpec &this_file) {
// When we're loaded from python, this_file will point to the file inside the
// python package directory. Replace it with the one in the lib directory.
#ifdef _WIN32
// On windows, we need to manually back out of the python tree, and go into
// the bin directory. This is pretty much the inverse of what ComputePythonDir
// does.
if (this_file.GetFileNameExtension() == ".pyd") {
2021-02-15 11:51:32 -09:00
this_file.RemoveLastPathComponent(); // _lldb.pyd or _lldb_d.pyd
this_file.RemoveLastPathComponent(); // native
2021-02-15 11:51:32 -09:00
this_file.RemoveLastPathComponent(); // lldb
llvm::StringRef libdir = LLDB_PYTHON_RELATIVE_LIBDIR;
for (auto it = llvm::sys::path::begin(libdir),
end = llvm::sys::path::end(libdir);
2021-02-15 11:51:32 -09:00
it != end; ++it)
this_file.RemoveLastPathComponent();
this_file.AppendPathComponent("bin");
this_file.AppendPathComponent("liblldb.dll");
}
#else
// The python file is a symlink, so we can find the real library by resolving
// it. We can do this unconditionally.
FileSystem::Instance().ResolveSymbolicLink(this_file, this_file);
#endif
}
llvm::StringRef ScriptInterpreterPython::GetPluginDescriptionStatic() {
return "Embedded Python interpreter";
}
void ScriptInterpreterPython::Initialize() {
#if LLDB_ENABLE_MTE
// Python's allocator (pymalloc) is not aware of Memory Tagging Extension
// (MTE) and crashes.
// https://bugs.python.org/issue43593
setenv("PYTHONMALLOC", "malloc", /*overwrite=*/true);
#endif
// When the plugin is a separate shared library, the SWIG wrapper lives in
// the plugin library, so the path helper that redirects lookups back to
// liblldb is unnecessary.
#if !LLDB_ENABLE_DYNAMIC_SCRIPTINTERPRETERS
HostInfo::SetSharedLibraryDirectoryHelper(
ScriptInterpreterPython::SharedLibraryDirectoryHelper);
#endif
PluginManager::RegisterPlugin(
GetPluginNameStatic(), GetPluginDescriptionStatic(),
lldb::eScriptLanguagePython, ScriptInterpreterPythonImpl::CreateInstance,
ScriptInterpreterPythonImpl::GetPythonDir);
ScriptInterpreterPythonImpl::Initialize();
ScriptInterpreterPythonInterfaces::Initialize();
}
void ScriptInterpreterPython::Terminate() {
ScriptInterpreterPythonInterfaces::Terminate();
PluginManager::UnregisterPlugin(ScriptInterpreterPythonImpl::CreateInstance);
}
ScriptInterpreterPythonImpl::Locker::Locker(
ScriptInterpreterPythonImpl *py_interpreter, uint16_t on_entry,
uint16_t on_leave, FileSP in, FileSP out, FileSP err)
: ScriptInterpreterLocker(),
<rdar://problem/13010007> Added the ability for OS plug-ins to lazily populate the thread this. The python OS plug-in classes can now implement the following method: class OperatingSystemPlugin: def create_thread(self, tid, context): # Return a dictionary for a new thread to create it on demand This will add a new thread to the thread list if it doesn't already exist. The example code in lldb/examples/python/operating_system.py has been updated to show how this call us used. Cleaned up the code in PythonDataObjects.cpp/h: - renamed all classes that started with PythonData* to be Python*. - renamed PythonArray to PythonList. Cleaned up the code to use inheritance where - Centralized the code that does ref counting in the PythonObject class to a single function. - Made the "bool PythonObject::Reset(PyObject *)" function be virtual so each subclass can correctly check to ensure a PyObject is of the right type before adopting the object. - Cleaned up all APIs and added new constructors for the Python* classes to they can all construct form: - PyObject * - const PythonObject & - const lldb::ScriptInterpreterObjectSP & Cleaned up code in ScriptInterpreterPython: - Made calling python functions safer by templatizing the production of value formats. Python specifies the value formats based on built in C types (long, long long, etc), and code often uses typedefs for uint32_t, uint64_t, etc when passing arguments down to python. We will now always produce correct value formats as the templatized code will "do the right thing" all the time. - Fixed issues with the ScriptInterpreterPython::Locker where entering the session and leaving the session had a bunch of issues that could cause the "lldb" module globals lldb.debugger, lldb.target, lldb.process, lldb.thread, and lldb.frame to not be initialized. llvm-svn: 172873
2013-01-18 14:41:08 -09:00
m_teardown_session((on_leave & TearDownSession) == TearDownSession),
m_python_interpreter(py_interpreter) {
DoAcquireLock();
if ((on_entry & InitSession) == InitSession) {
if (!DoInitSession(on_entry, in, out, err)) {
// Don't teardown the session if we didn't init it.
m_teardown_session = false;
}
}
}
bool ScriptInterpreterPythonImpl::Locker::DoAcquireLock() {
m_GILState = PyGILState_Ensure();
[lldb] Rename formatv verbose log call, misc log cleanups [NFC] (#186951) lldb had three preprocessor defines for logging, LLDB_LOG - formatv style argument LLDB_LOGF - printf style argument LLDB_LOGV - formatv style argument, only when verbose enabled If you weren't looking at Log.h and the definition of these three, and wanted to log something with formatv, it was easy to use LLDB_LOGV by accident. We just had a situation where an important log statement wasn't logging and it turned out to be this. This is fragile if you aren't looking at the header directly, so I'd like to make this more explicit. My proposal: LLDB_LOG - formatv style argument LLDB_LOG_VERBOSE - formatv style argument, only when verbose enabled LLDB_LOGF - printf style argument LLDB_LOGF_VERBOSE - printf style argument, only when verbose enabled The new fouth one is to remove several places where we do `if (log && log->GetVerbose()) LLDB_LOGF (...)` in the sources today, and make both styles consistent. This PR implements that change, mechanically changing all LLDB_LOGV's to LLDB_LOG_VERBOSE. It also updates many of the `if (log && log->GetVerbose()) LLDB_LOGF`'s. Some uses of this conditional expression do extra calculations in addition to logging, and so those were left as-is so we're not doing throwaway work when running without verbose logging. There were many instances throughout lldb where callers are still doing `if (log) LLDB_LOG*(...)`, a remnant of when all calls were to the `Log` object's `Printf()` method, and you had to check if your local Log* pointer was non-nullptr before calling the method. I removed those, again keeping ones where work for logging is done in the block of code. The code changes are all mechanical and uninteresting, but the question of whether this naming change is widely agreed on is maybe worth discussing.
2026-03-18 15:31:33 -08:00
LLDB_LOG_VERBOSE(GetLog(LLDBLog::Script),
"Ensured PyGILState. Previous state = {0}",
m_GILState == PyGILState_UNLOCKED ? "unlocked" : "locked");
// we need to save the thread state when we first start the command because
// we might decide to interrupt it while some action is taking place outside
// of Python (e.g. printing to screen, waiting for the network, ...) in that
// case, _PyThreadState_Current will be NULL - and we would be unable to set
// the asynchronous exception - not a desirable situation
m_python_interpreter->SetThreadState(PyThreadState_Get());
m_python_interpreter->IncrementLockCount();
return true;
}
bool ScriptInterpreterPythonImpl::Locker::DoInitSession(uint16_t on_entry_flags,
FileSP in, FileSP out,
FileSP err) {
if (!m_python_interpreter)
<rdar://problem/13010007> Added the ability for OS plug-ins to lazily populate the thread this. The python OS plug-in classes can now implement the following method: class OperatingSystemPlugin: def create_thread(self, tid, context): # Return a dictionary for a new thread to create it on demand This will add a new thread to the thread list if it doesn't already exist. The example code in lldb/examples/python/operating_system.py has been updated to show how this call us used. Cleaned up the code in PythonDataObjects.cpp/h: - renamed all classes that started with PythonData* to be Python*. - renamed PythonArray to PythonList. Cleaned up the code to use inheritance where - Centralized the code that does ref counting in the PythonObject class to a single function. - Made the "bool PythonObject::Reset(PyObject *)" function be virtual so each subclass can correctly check to ensure a PyObject is of the right type before adopting the object. - Cleaned up all APIs and added new constructors for the Python* classes to they can all construct form: - PyObject * - const PythonObject & - const lldb::ScriptInterpreterObjectSP & Cleaned up code in ScriptInterpreterPython: - Made calling python functions safer by templatizing the production of value formats. Python specifies the value formats based on built in C types (long, long long, etc), and code often uses typedefs for uint32_t, uint64_t, etc when passing arguments down to python. We will now always produce correct value formats as the templatized code will "do the right thing" all the time. - Fixed issues with the ScriptInterpreterPython::Locker where entering the session and leaving the session had a bunch of issues that could cause the "lldb" module globals lldb.debugger, lldb.target, lldb.process, lldb.thread, and lldb.frame to not be initialized. llvm-svn: 172873
2013-01-18 14:41:08 -09:00
return false;
return m_python_interpreter->EnterSession(on_entry_flags, in, out, err);
}
bool ScriptInterpreterPythonImpl::Locker::DoFreeLock() {
[lldb] Rename formatv verbose log call, misc log cleanups [NFC] (#186951) lldb had three preprocessor defines for logging, LLDB_LOG - formatv style argument LLDB_LOGF - printf style argument LLDB_LOGV - formatv style argument, only when verbose enabled If you weren't looking at Log.h and the definition of these three, and wanted to log something with formatv, it was easy to use LLDB_LOGV by accident. We just had a situation where an important log statement wasn't logging and it turned out to be this. This is fragile if you aren't looking at the header directly, so I'd like to make this more explicit. My proposal: LLDB_LOG - formatv style argument LLDB_LOG_VERBOSE - formatv style argument, only when verbose enabled LLDB_LOGF - printf style argument LLDB_LOGF_VERBOSE - printf style argument, only when verbose enabled The new fouth one is to remove several places where we do `if (log && log->GetVerbose()) LLDB_LOGF (...)` in the sources today, and make both styles consistent. This PR implements that change, mechanically changing all LLDB_LOGV's to LLDB_LOG_VERBOSE. It also updates many of the `if (log && log->GetVerbose()) LLDB_LOGF`'s. Some uses of this conditional expression do extra calculations in addition to logging, and so those were left as-is so we're not doing throwaway work when running without verbose logging. There were many instances throughout lldb where callers are still doing `if (log) LLDB_LOG*(...)`, a remnant of when all calls were to the `Log` object's `Printf()` method, and you had to check if your local Log* pointer was non-nullptr before calling the method. I removed those, again keeping ones where work for logging is done in the block of code. The code changes are all mechanical and uninteresting, but the question of whether this naming change is widely agreed on is maybe worth discussing.
2026-03-18 15:31:33 -08:00
LLDB_LOG_VERBOSE(GetLog(LLDBLog::Script),
"Releasing PyGILState. Returning to state = {0}",
m_GILState == PyGILState_UNLOCKED ? "unlocked" : "locked");
PyGILState_Release(m_GILState);
m_python_interpreter->DecrementLockCount();
return true;
}
bool ScriptInterpreterPythonImpl::Locker::DoTearDownSession() {
if (!m_python_interpreter)
return false;
m_python_interpreter->LeaveSession();
return true;
}
ScriptInterpreterPythonImpl::Locker::~Locker() {
if (m_teardown_session)
DoTearDownSession();
DoFreeLock();
}
ScriptInterpreterPythonImpl::ScriptInterpreterPythonImpl(Debugger &debugger)
: ScriptInterpreterPython(debugger), m_saved_stdin(), m_saved_stdout(),
m_saved_stderr(), m_main_module(),
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
m_session_dict(PyInitialValue::Invalid),
m_sys_module_dict(PyInitialValue::Invalid), m_run_one_line_function(),
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
m_run_one_line_str_global(),
m_dictionary_name(m_debugger.GetInstanceName()),
m_active_io_handler(eIOHandlerNone), m_session_is_active(false),
m_pty_secondary_is_open(false), m_valid_session(true), m_lock_count(0),
m_command_thread_state(nullptr) {
m_dictionary_name.append("_dict");
StreamString run_string;
run_string.Printf("%s = dict()", m_dictionary_name.c_str());
Locker locker(this, Locker::AcquireLock, Locker::FreeAcquiredLock);
RunSimpleString(run_string.GetData());
run_string.Clear();
run_string.Printf(
"run_one_line (%s, 'import copy, keyword, os, re, sys, uuid, lldb')",
m_dictionary_name.c_str());
RunSimpleString(run_string.GetData());
// Reloading modules requires a different syntax in Python 2 and Python 3.
// This provides a consistent syntax no matter what version of Python.
run_string.Clear();
run_string.Printf(
"run_one_line (%s, 'from importlib import reload as reload_module')",
m_dictionary_name.c_str());
RunSimpleString(run_string.GetData());
// WARNING: temporary code that loads Cocoa formatters - this should be done
// on a per-platform basis rather than loading the whole set and letting the
// individual formatter classes exploit APIs to check whether they can/cannot
// do their task
run_string.Clear();
run_string.Printf(
"run_one_line (%s, 'import lldb.formatters, lldb.formatters.cpp')",
m_dictionary_name.c_str());
RunSimpleString(run_string.GetData());
run_string.Clear();
run_string.Printf("run_one_line (%s, 'import lldb.embedded_interpreter; from "
"lldb.embedded_interpreter import run_python_interpreter; "
"from lldb.embedded_interpreter import run_one_line')",
m_dictionary_name.c_str());
RunSimpleString(run_string.GetData());
run_string.Clear();
// Configure pydoc (built-in module) to use the "plain" pager. The default one
// doesn't play nice with the statusline.
run_string.Printf("run_one_line (%s, 'import pydoc; pydoc.pager = "
"pydoc.plainpager')",
m_dictionary_name.c_str());
RunSimpleString(run_string.GetData());
run_string.Clear();
run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64
"')",
m_dictionary_name.c_str(), m_debugger.GetID());
RunSimpleString(run_string.GetData());
}
/// A Python sys.stdout/stderr file backed by a pipe whose read end is drained
/// by a reader thread that writes to the debugger's terminal under the output
/// lock (Debugger::PrintAsync). Handing Python the raw terminal descriptor
/// instead lets a script's print() race the statusline, which redraws on the
/// event thread under that lock. Its cursor save/restore then rewinds over and
/// eats the script's output.
class ScriptInterpreterPythonImpl::SessionIORedirect {
public:
static std::unique_ptr<SessionIORedirect> Create(lldb::user_id_t debugger_id,
bool is_stdout) {
Pipe pipe;
if (pipe.CreateNew().Fail())
return nullptr;
std::unique_ptr<SessionIORedirect> redirect(
new SessionIORedirect(debugger_id, is_stdout));
#if defined(_WIN32)
lldb::file_t read_handle = pipe.GetReadNativeHandle();
pipe.ReleaseReadFileDescriptor();
std::unique_ptr<Connection> conn =
std::make_unique<ConnectionGenericFile>(read_handle, true);
#else
std::unique_ptr<Connection> conn =
std::make_unique<ConnectionFileDescriptor>(
pipe.ReleaseReadFileDescriptor(), /*owns_fd=*/true);
#endif
if (!conn->IsConnected())
return nullptr;
redirect->m_communication.SetConnection(std::move(conn));
redirect->m_communication.SetReadThreadBytesReceivedCallback(
ReadThreadBytesReceived, redirect.get());
if (!redirect->m_communication.StartReadThread())
return nullptr;
redirect->m_connected = true;
// The write end is owned here. Python only borrows its descriptor.
redirect->m_write_file_sp = std::make_shared<NativeFile>(
pipe.ReleaseWriteFileDescriptor(), File::eOpenOptionWriteOnly,
NativeFile::Owned);
return redirect;
}
~SessionIORedirect() {
if (!m_connected)
return;
// Close the write end so the reader sees EOF and exits, then join it.
if (m_write_file_sp)
m_write_file_sp->Close();
m_communication.JoinReadThread();
m_communication.Disconnect();
}
int GetWriteDescriptor() const {
return m_write_file_sp ? m_write_file_sp->GetDescriptor()
: File::kInvalidDescriptor;
}
private:
SessionIORedirect(lldb::user_id_t debugger_id, bool is_stdout)
: m_debugger_id(debugger_id), m_is_stdout(is_stdout),
m_communication("lldb.ScriptInterpreterPython.io-redirect") {}
static void ReadThreadBytesReceived(void *baton, const void *src,
size_t src_len) {
if (!src || !src_len)
return;
auto *self = static_cast<SessionIORedirect *>(baton);
if (lldb::DebuggerSP debugger_sp =
Debugger::FindDebuggerWithID(self->m_debugger_id))
debugger_sp->PrintAsync(static_cast<const char *>(src), src_len,
self->m_is_stdout);
}
lldb::user_id_t m_debugger_id;
bool m_is_stdout;
lldb::FileSP m_write_file_sp;
ThreadedCommunication m_communication;
bool m_connected = false;
};
ScriptInterpreterPythonImpl::~ScriptInterpreterPythonImpl() {
// the session dictionary may hold objects with complex state which means
// that they may need to be torn down with some level of smarts and that, in
// turn, requires a valid thread state force Python to procure itself such a
// thread state, nuke the session dictionary and then release it for others
// to use and proceed with the rest of the shutdown
auto gil_state = PyGILState_Ensure();
m_session_dict.Reset();
PyGILState_Release(gil_state);
}
void ScriptInterpreterPythonImpl::IOHandlerActivated(IOHandler &io_handler,
bool interactive) {
const char *instructions = nullptr;
switch (m_active_io_handler) {
case eIOHandlerNone:
break;
case eIOHandlerBreakpoint:
instructions = R"(Enter your Python command(s). Type 'DONE' to end.
def function (frame, bp_loc, internal_dict):
"""frame: the lldb.SBFrame for the location at which you stopped
bp_loc: an lldb.SBBreakpointLocation for the breakpoint location information
internal_dict: an LLDB support object not to be used"""
)";
break;
case eIOHandlerWatchpoint:
instructions = "Enter your Python command(s). Type 'DONE' to end.\n";
break;
}
if (instructions && interactive) {
if (LockableStreamFileSP stream_sp = io_handler.GetOutputStreamFileSP()) {
LockedStreamFile locked_stream = stream_sp->Lock();
locked_stream.PutCString(instructions);
locked_stream.Flush();
}
}
}
void ScriptInterpreterPythonImpl::IOHandlerInputComplete(IOHandler &io_handler,
std::string &data) {
io_handler.SetIsDone(true);
bool batch_mode = m_debugger.GetCommandInterpreter().GetBatchCommandMode();
switch (m_active_io_handler) {
case eIOHandlerNone:
break;
case eIOHandlerBreakpoint: {
std::vector<std::reference_wrapper<BreakpointOptions>> *bp_options_vec =
(std::vector<std::reference_wrapper<BreakpointOptions>> *)
io_handler.GetUserData();
for (BreakpointOptions &bp_options : *bp_options_vec) {
auto data_up = std::make_unique<CommandDataPython>();
if (!data_up)
break;
data_up->user_source.SplitIntoLines(data);
if (GenerateBreakpointCommandCallbackData(data_up->user_source,
data_up->script_source,
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
/*has_extra_args=*/false,
/*is_callback=*/false)
.Success()) {
auto baton_sp = std::make_shared<BreakpointOptions::CommandBaton>(
std::move(data_up));
bp_options.SetCallback(
ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
} else if (!batch_mode) {
if (LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP()) {
LockedStreamFile locked_stream = error_sp->Lock();
locked_stream.Printf("Warning: No command attached to breakpoint.\n");
}
}
}
m_active_io_handler = eIOHandlerNone;
} break;
case eIOHandlerWatchpoint: {
WatchpointOptions *wp_options =
(WatchpointOptions *)io_handler.GetUserData();
auto data_up = std::make_unique<WatchpointOptions::CommandData>();
data_up->user_source.SplitIntoLines(data);
if (GenerateWatchpointCommandCallbackData(data_up->user_source,
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
data_up->script_source,
/*is_callback=*/false)) {
auto baton_sp =
std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
wp_options->SetCallback(
ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp);
} else if (!batch_mode) {
if (LockableStreamFileSP error_sp = io_handler.GetErrorStreamFileSP()) {
LockedStreamFile locked_stream = error_sp->Lock();
locked_stream.Printf("Warning: No command attached to breakpoint.\n");
}
}
m_active_io_handler = eIOHandlerNone;
} break;
}
}
lldb::ScriptInterpreterSP
ScriptInterpreterPythonImpl::CreateInstance(Debugger &debugger) {
return std::make_shared<ScriptInterpreterPythonImpl>(debugger);
}
void ScriptInterpreterPythonImpl::LeaveSession() {
Log *log = GetLog(LLDBLog::Script);
if (log)
log->PutCString("ScriptInterpreterPythonImpl::LeaveSession()");
// Unset the LLDB global variables.
RunSimpleString("lldb.debugger = None; lldb.target = None; lldb.process "
"= None; lldb.thread = None; lldb.frame = None");
// checking that we have a valid thread state - since we use our own
// threading and locking in some (rare) cases during cleanup Python may end
// up believing we have no thread state and PyImport_AddModule will crash if
// that is the case - since that seems to only happen when destroying the
// SBDebugger, we can make do without clearing up stdout and stderr
if (PyThreadState_GetDict()) {
PythonDictionary &sys_module_dict = GetSysModuleDictionary();
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (sys_module_dict.IsValid()) {
// Flush the pipe-backed wrappers while they are still sys.stdout/stderr.
// Line buffering already flushes on each newline, but a trailing
// unterminated line would otherwise be stranded (and later flushed into
// a closed descriptor) once we close the pipe write end below.
auto flush_redirect = [&](const char *py_name,
std::unique_ptr<SessionIORedirect> &redirect) {
if (!redirect)
return;
PythonObject file =
sys_module_dict.GetItemForKey(PythonString(py_name));
if (!file.IsValid())
return;
if (llvm::Expected<PythonObject> result = file.CallMethod("flush"))
(void)result;
else
llvm::consumeError(result.takeError());
};
flush_redirect("stdout", m_stdout_redirect);
flush_redirect("stderr", m_stderr_redirect);
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (m_saved_stdin.IsValid()) {
sys_module_dict.SetItemForKey(PythonString("stdin"), m_saved_stdin);
m_saved_stdin.Reset();
}
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (m_saved_stdout.IsValid()) {
sys_module_dict.SetItemForKey(PythonString("stdout"), m_saved_stdout);
m_saved_stdout.Reset();
}
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (m_saved_stderr.IsValid()) {
sys_module_dict.SetItemForKey(PythonString("stderr"), m_saved_stderr);
m_saved_stderr.Reset();
}
}
}
// Tear down the pipe redirects (closes each write end and joins its reader).
// The wrappers were flushed above, so nothing buffered is lost.
m_stdout_redirect.reset();
m_stderr_redirect.reset();
m_session_is_active = false;
}
bool ScriptInterpreterPythonImpl::RedirectTerminalHandleThroughLock(
const char *py_name, PythonObject &save_file, const char *mode,
File &file) {
const bool is_stdout = ::strcmp(py_name, "stdout") == 0;
if (!is_stdout && ::strcmp(py_name, "stderr") != 0)
return false;
// The statusline is the only writer that races Python's terminal output.
// When it isn't drawing there is nothing to serialize against, so keep the
// normal wrapping and skip the reader thread and pipe.
if (!m_debugger.StatuslineSupported())
return false;
// Only the debugger's own terminal races the statusline. A redirect to a
// pipe or user file (a different descriptor) is wrapped normally.
lldb::FileSP debugger_file =
is_stdout ? m_debugger.GetOutputFileSP() : m_debugger.GetErrorFileSP();
int fd = file.GetDescriptor();
if (!debugger_file || fd == File::kInvalidDescriptor ||
fd != debugger_file->GetDescriptor())
return false;
std::unique_ptr<SessionIORedirect> &redirect =
is_stdout ? m_stdout_redirect : m_stderr_redirect;
redirect = SessionIORedirect::Create(m_debugger.GetID(), is_stdout);
if (!redirect)
return false;
// Line-buffer the wrapper so each print() reaches the reader (and the
// terminal) promptly: the pipe descriptor is not a tty, so the default
// buffering would hold output back until the buffer filled.
PyObject *pipe_file = PyFile_FromFd(
redirect->GetWriteDescriptor(), nullptr, mode, /*buffering=*/1,
/*encoding=*/nullptr, /*errors=*/"ignore", /*newline=*/nullptr,
/*closefd=*/0);
if (!pipe_file) {
// Fall back to the raw descriptor. That reopens the statusline race, so
// leave a breadcrumb rather than failing silently.
LLDB_LOG(GetLog(LLDBLog::Script),
"failed to wrap sys.{0} on a synchronized pipe; falling back to "
"the unsynchronized terminal descriptor",
py_name);
PyErr_Clear();
redirect.reset();
return false;
}
PythonObject new_file(PyRefType::Owned, pipe_file);
PythonDictionary &sys_module_dict = GetSysModuleDictionary();
save_file = sys_module_dict.GetItemForKey(PythonString(py_name));
sys_module_dict.SetItemForKey(PythonString(py_name), new_file);
return true;
}
bool ScriptInterpreterPythonImpl::SetStdHandle(FileSP file_sp,
const char *py_name,
PythonObject &save_file,
const char *mode,
bool serialize_terminal_output) {
if (!file_sp || !*file_sp) {
save_file.Reset();
return false;
}
File &file = *file_sp;
// When stdout/stderr point at the debugger's own terminal, route Python's
// output through a pipe drained under the output lock so a script's print()
// cannot race the statusline. Any other target keeps the normal wrapping.
if (serialize_terminal_output &&
RedirectTerminalHandleThroughLock(py_name, save_file, mode, file))
return true;
// Flush the file before giving it to python to avoid interleaved output.
file.Flush();
PythonDictionary &sys_module_dict = GetSysModuleDictionary();
auto new_file = PythonFile::FromFile(file, mode);
if (!new_file) {
LLDB_LOG_ERROR(GetLog(LLDBLog::Script), new_file.takeError(),
"ScriptInterpreterPythonImpl::SetStdHandle failed to wrap "
"sys.{1}: {0}",
py_name);
return false;
}
save_file = sys_module_dict.GetItemForKey(PythonString(py_name));
sys_module_dict.SetItemForKey(PythonString(py_name), new_file.get());
return true;
}
bool ScriptInterpreterPythonImpl::EnterSession(uint16_t on_entry_flags,
FileSP in_sp, FileSP out_sp,
FileSP err_sp) {
// If we have already entered the session, without having officially 'left'
// it, then there is no need to 'enter' it again.
Log *log = GetLog(LLDBLog::Script);
if (m_session_is_active) {
LLDB_LOGF(
log,
"ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16
") session is already active, returning without doing anything",
on_entry_flags);
return false;
}
LLDB_LOGF(
log,
"ScriptInterpreterPythonImpl::EnterSession(on_entry_flags=0x%" PRIx16 ")",
on_entry_flags);
m_session_is_active = true;
StreamString run_string;
if (on_entry_flags & Locker::InitGlobals) {
run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64,
m_dictionary_name.c_str(), m_debugger.GetID());
run_string.Printf(
"; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")",
m_debugger.GetID());
run_string.PutCString("; lldb.target = lldb.debugger.GetSelectedTarget()");
run_string.PutCString("; lldb.process = lldb.target.GetProcess()");
run_string.PutCString("; lldb.thread = lldb.process.GetSelectedThread ()");
run_string.PutCString("; lldb.frame = lldb.thread.GetSelectedFrame ()");
run_string.PutCString("')");
} else {
// If we aren't initing the globals, we should still always set the
// debugger (since that is always unique.)
run_string.Printf("run_one_line (%s, 'lldb.debugger_unique_id = %" PRIu64,
m_dictionary_name.c_str(), m_debugger.GetID());
run_string.Printf(
"; lldb.debugger = lldb.SBDebugger.FindDebuggerWithID (%" PRIu64 ")",
m_debugger.GetID());
run_string.PutCString("')");
}
RunSimpleString(run_string.GetData());
run_string.Clear();
PythonDictionary &sys_module_dict = GetSysModuleDictionary();
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (sys_module_dict.IsValid()) {
lldb::FileSP top_in_sp;
lldb::LockableStreamFileSP top_out_sp, top_err_sp;
if (!in_sp || !out_sp || !err_sp || !*in_sp || !*out_sp || !*err_sp)
m_debugger.AdoptTopIOHandlerFilesIfInvalid(top_in_sp, top_out_sp,
top_err_sp);
if (on_entry_flags & Locker::NoSTDIN) {
m_saved_stdin.Reset();
} else {
if (!SetStdHandle(in_sp, "stdin", m_saved_stdin, "r",
/*serialize_terminal_output=*/false)) {
if (top_in_sp)
SetStdHandle(top_in_sp, "stdin", m_saved_stdin, "r",
/*serialize_terminal_output=*/false);
}
}
// Serialize terminal output for every session except those that opt out
// with NoOutputRedirect (see the flag for why).
const bool serialize_terminal_output =
!(on_entry_flags & Locker::NoOutputRedirect);
if (!SetStdHandle(out_sp, "stdout", m_saved_stdout, "w",
serialize_terminal_output)) {
if (top_out_sp)
SetStdHandle(top_out_sp->GetUnlockedFileSP(), "stdout", m_saved_stdout,
"w", serialize_terminal_output);
}
if (!SetStdHandle(err_sp, "stderr", m_saved_stderr, "w",
serialize_terminal_output)) {
if (top_err_sp)
SetStdHandle(top_err_sp->GetUnlockedFileSP(), "stderr", m_saved_stderr,
"w", serialize_terminal_output);
}
}
if (PyErr_Occurred())
PyErr_Clear();
<rdar://problem/13010007> Added the ability for OS plug-ins to lazily populate the thread this. The python OS plug-in classes can now implement the following method: class OperatingSystemPlugin: def create_thread(self, tid, context): # Return a dictionary for a new thread to create it on demand This will add a new thread to the thread list if it doesn't already exist. The example code in lldb/examples/python/operating_system.py has been updated to show how this call us used. Cleaned up the code in PythonDataObjects.cpp/h: - renamed all classes that started with PythonData* to be Python*. - renamed PythonArray to PythonList. Cleaned up the code to use inheritance where - Centralized the code that does ref counting in the PythonObject class to a single function. - Made the "bool PythonObject::Reset(PyObject *)" function be virtual so each subclass can correctly check to ensure a PyObject is of the right type before adopting the object. - Cleaned up all APIs and added new constructors for the Python* classes to they can all construct form: - PyObject * - const PythonObject & - const lldb::ScriptInterpreterObjectSP & Cleaned up code in ScriptInterpreterPython: - Made calling python functions safer by templatizing the production of value formats. Python specifies the value formats based on built in C types (long, long long, etc), and code often uses typedefs for uint32_t, uint64_t, etc when passing arguments down to python. We will now always produce correct value formats as the templatized code will "do the right thing" all the time. - Fixed issues with the ScriptInterpreterPython::Locker where entering the session and leaving the session had a bunch of issues that could cause the "lldb" module globals lldb.debugger, lldb.target, lldb.process, lldb.thread, and lldb.frame to not be initialized. llvm-svn: 172873
2013-01-18 14:41:08 -09:00
return true;
}
PythonModule &ScriptInterpreterPythonImpl::GetMainModule() {
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (!m_main_module.IsValid())
m_main_module = unwrapIgnoringErrors(PythonModule::Import("__main__"));
return m_main_module;
}
PythonDictionary &ScriptInterpreterPythonImpl::GetSessionDictionary() {
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (m_session_dict.IsValid())
return m_session_dict;
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
PythonObject &main_module = GetMainModule();
if (!main_module.IsValid())
return m_session_dict;
PythonDictionary main_dict(PyRefType::Borrowed,
PyModule_GetDict(main_module.get()));
if (!main_dict.IsValid())
return m_session_dict;
m_session_dict = unwrapIgnoringErrors(
As<PythonDictionary>(main_dict.GetItem(m_dictionary_name)));
return m_session_dict;
}
PythonDictionary &ScriptInterpreterPythonImpl::GetSysModuleDictionary() {
if (m_sys_module_dict.IsValid())
return m_sys_module_dict;
PythonModule sys_module = unwrapIgnoringErrors(PythonModule::Import("sys"));
m_sys_module_dict = sys_module.GetDictionary();
return m_sys_module_dict;
}
llvm::Expected<unsigned>
ScriptInterpreterPythonImpl::GetMaxPositionalArgumentsForCallable(
const llvm::StringRef &callable_name) {
if (callable_name.empty()) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"called with empty callable name.");
}
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
auto dict = PythonModule::MainModule().ResolveName<PythonDictionary>(
m_dictionary_name);
auto pfunc = PythonObject::ResolveNameWithDictionary<PythonCallable>(
callable_name, dict);
if (!pfunc.IsAllocated()) {
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"can't find callable: %s",
callable_name.str().c_str());
}
llvm::Expected<PythonCallable::ArgInfo> arg_info = pfunc.GetArgInfo();
[lldb] Reimplement PythonCallable::GetArgInfo without executing Python code (#213378) `b05a5d0a` added an arity-trimming step to the shared `ScriptedPythonInterface::Dispatch`: extensions are now allowed to define methods with trailing parameters as optional (`num_children(self)` vs. `num_children(self, max_count)`), so before calling into a method, `Dispatch` needs to know how many positional arguments it actually accepts and drop any trailing ones we'd otherwise pass. That check calls `PythonCallable::GetArgInfo`, which ran a whole embedded Python script through `inspect.signature` on every call, since every scripted-extension dispatch goes through it. For the common case `GetArgInfo()` actually needs to handle fast (plain Python functions/methods, classes used as constructors, and callable instances defining `__call__`, i.e. everything `Dispatch<T>()` and `CreatePluginObject()` ever pass it), the answer is available as plain data attributes, with no Python bytecode execution required: `__func__`/`__self__` to unwrap bound methods, and `__code__`'s `co_argcount`/`co_flags` for the actual positional-argument count and varargs bit. For a class, that means `__init__` -- except a class may customize `__new__` instead and leave `__init__` untouched, in which case `object.__init__` becomes lenient about extra arguments. Anything else still lacking `__code__` can fall back to the original `inspect.signature()`-based implementation, now exposed as `PythonCallable::GetArgInfoFromInspectSignature()` so it can be used separately. rdar://183776556 --------- Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
2026-08-01 15:23:21 -08:00
if (!arg_info) {
// `-f` may point at a builtin, unlike other GetArgInfo() callers.
LLDB_LOG_ERROR(GetLog(LLDBLog::Script), arg_info.takeError(),
"GetArgInfo failed for callable {1}, falling back to "
"inspect.signature: {0}",
callable_name);
arg_info = PythonCallable::GetArgInfoFromInspectSignature(pfunc);
}
if (!arg_info)
return arg_info.takeError();
return arg_info.get().max_positional_args;
}
<rdar://problem/13010007> Added the ability for OS plug-ins to lazily populate the thread this. The python OS plug-in classes can now implement the following method: class OperatingSystemPlugin: def create_thread(self, tid, context): # Return a dictionary for a new thread to create it on demand This will add a new thread to the thread list if it doesn't already exist. The example code in lldb/examples/python/operating_system.py has been updated to show how this call us used. Cleaned up the code in PythonDataObjects.cpp/h: - renamed all classes that started with PythonData* to be Python*. - renamed PythonArray to PythonList. Cleaned up the code to use inheritance where - Centralized the code that does ref counting in the PythonObject class to a single function. - Made the "bool PythonObject::Reset(PyObject *)" function be virtual so each subclass can correctly check to ensure a PyObject is of the right type before adopting the object. - Cleaned up all APIs and added new constructors for the Python* classes to they can all construct form: - PyObject * - const PythonObject & - const lldb::ScriptInterpreterObjectSP & Cleaned up code in ScriptInterpreterPython: - Made calling python functions safer by templatizing the production of value formats. Python specifies the value formats based on built in C types (long, long long, etc), and code often uses typedefs for uint32_t, uint64_t, etc when passing arguments down to python. We will now always produce correct value formats as the templatized code will "do the right thing" all the time. - Fixed issues with the ScriptInterpreterPython::Locker where entering the session and leaving the session had a bunch of issues that could cause the "lldb" module globals lldb.debugger, lldb.target, lldb.process, lldb.thread, and lldb.frame to not be initialized. llvm-svn: 172873
2013-01-18 14:41:08 -09:00
static std::string GenerateUniqueName(const char *base_name_wanted,
uint32_t &functions_counter,
const void *name_token = nullptr) {
StreamString sstr;
if (!base_name_wanted)
return std::string();
if (!name_token)
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
sstr.Printf("%s_%d", base_name_wanted, functions_counter++);
else
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
sstr.Printf("%s_%p", base_name_wanted, name_token);
return std::string(sstr.GetString());
}
bool ScriptInterpreterPythonImpl::GetEmbeddedInterpreterModuleObjects() {
if (m_run_one_line_function.IsValid())
return true;
PythonObject module(PyRefType::Borrowed,
PyImport_AddModule("lldb.embedded_interpreter"));
if (!module.IsValid())
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
return false;
PythonDictionary module_dict(PyRefType::Borrowed,
PyModule_GetDict(module.get()));
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (!module_dict.IsValid())
return false;
m_run_one_line_function =
module_dict.GetItemForKey(PythonString("run_one_line"));
m_run_one_line_str_global =
module_dict.GetItemForKey(PythonString("g_run_one_line_str"));
return m_run_one_line_function.IsValid();
<rdar://problem/13010007> Added the ability for OS plug-ins to lazily populate the thread this. The python OS plug-in classes can now implement the following method: class OperatingSystemPlugin: def create_thread(self, tid, context): # Return a dictionary for a new thread to create it on demand This will add a new thread to the thread list if it doesn't already exist. The example code in lldb/examples/python/operating_system.py has been updated to show how this call us used. Cleaned up the code in PythonDataObjects.cpp/h: - renamed all classes that started with PythonData* to be Python*. - renamed PythonArray to PythonList. Cleaned up the code to use inheritance where - Centralized the code that does ref counting in the PythonObject class to a single function. - Made the "bool PythonObject::Reset(PyObject *)" function be virtual so each subclass can correctly check to ensure a PyObject is of the right type before adopting the object. - Cleaned up all APIs and added new constructors for the Python* classes to they can all construct form: - PyObject * - const PythonObject & - const lldb::ScriptInterpreterObjectSP & Cleaned up code in ScriptInterpreterPython: - Made calling python functions safer by templatizing the production of value formats. Python specifies the value formats based on built in C types (long, long long, etc), and code often uses typedefs for uint32_t, uint64_t, etc when passing arguments down to python. We will now always produce correct value formats as the templatized code will "do the right thing" all the time. - Fixed issues with the ScriptInterpreterPython::Locker where entering the session and leaving the session had a bunch of issues that could cause the "lldb" module globals lldb.debugger, lldb.target, lldb.process, lldb.thread, and lldb.frame to not be initialized. llvm-svn: 172873
2013-01-18 14:41:08 -09:00
}
bool ScriptInterpreterPythonImpl::ExecuteOneLine(
llvm::StringRef command, CommandReturnObject *result,
const ExecuteScriptOptions &options) {
std::string command_str = command.str();
if (!m_valid_session)
return false;
if (!command.empty()) {
// We want to call run_one_line, passing in the dictionary and the command
// string. We cannot do this through RunSimpleString here because the
// command string may contain escaped characters, and putting it inside
// another string to pass to RunSimpleString messes up the escaping. So
// we use the following more complicated method to pass the command string
// directly down to Python.
llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
io_redirect_or_error = ScriptInterpreterIORedirect::Create(
options.GetEnableIO(), m_debugger, result);
if (!io_redirect_or_error) {
if (result)
result->AppendErrorWithFormatv(
"failed to redirect I/O: {0}\n",
llvm::fmt_consume(io_redirect_or_error.takeError()));
else
llvm::consumeError(io_redirect_or_error.takeError());
return false;
}
ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error;
bool success = false;
{
// WARNING! It's imperative that this RAII scope be as tight as
// possible. In particular, the scope must end *before* we try to join
// the read thread. The reason for this is that a pre-requisite for
// joining the read thread is that we close the write handle (to break
// the pipe and cause it to wake up and exit). But acquiring the GIL as
// below will redirect Python's stdio to use this same handle. If we
// close the handle while Python is still using it, bad things will
// happen.
Locker locker(
this,
Locker::AcquireLock | Locker::InitSession |
(options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
((result && result->GetInteractive()) ? 0 : Locker::NoSTDIN),
Locker::FreeAcquiredLock | Locker::TearDownSession,
io_redirect.GetInputFile(), io_redirect.GetOutputFile(),
io_redirect.GetErrorFile());
// Find the correct script interpreter dictionary in the main module.
PythonDictionary &session_dict = GetSessionDictionary();
if (session_dict.IsValid()) {
if (GetEmbeddedInterpreterModuleObjects()) {
if (PyCallable_Check(m_run_one_line_function.get())) {
PythonObject pargs(
PyRefType::Owned,
Py_BuildValue("(Os)", session_dict.get(), command_str.c_str()));
if (pargs.IsValid()) {
PythonObject return_value(
PyRefType::Owned,
PyObject_CallObject(m_run_one_line_function.get(),
pargs.get()));
if (return_value.IsValid())
success = true;
else if (options.GetMaskoutErrors() && PyErr_Occurred()) {
PyErr_Print();
PyErr_Clear();
}
}
}
}
}
io_redirect.Flush();
}
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (success)
return true;
// The one-liner failed. Append the error message.
if (result) {
result->AppendErrorWithFormat("python failed attempting to evaluate '%s'",
command_str.c_str());
}
return false;
}
if (result)
result->AppendError("empty command passed to python\n");
return false;
}
void ScriptInterpreterPythonImpl::ExecuteInterpreterLoop() {
LLDB_SCOPED_TIMER();
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
Debugger &debugger = m_debugger;
// At the moment, the only time the debugger does not have an input file
// handle is when this is called directly from Python, in which case it is
// both dangerous and unnecessary (not to mention confusing) to try to embed
// a running interpreter loop inside the already running Python interpreter
// loop, so we won't do it.
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (!debugger.GetInputFile().IsValid())
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
return;
IOHandlerSP io_handler_sp(new IOHandlerPythonInterpreter(debugger, this));
if (io_handler_sp) {
debugger.RunIOHandlerAsync(io_handler_sp);
}
}
bool ScriptInterpreterPythonImpl::Interrupt() {
[lldb] Fix that the embedded Python REPL crashes if it receives SIGINT When LLDB receives a SIGINT while running the embedded Python REPL it currently just crashes in ScriptInterpreterPythonImpl::Interrupt with an error such as the one below: Fatal Python error: PyThreadState_Get: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) The faulty code that causes this error is this part of ScriptInterpreterPythonImpl::Interrupt: PyThreadState *state = PyThreadState_GET(); if (!state) state = GetThreadState(); if (state) { long tid = state->thread_id; PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); The obvious fix I tried is to just acquire the GIL before this code is running which fixes the crash but the KeyboardInterrupt we want to raise immediately is actually just queued and would only be raised once the next line of input has been parsed (which e.g. won't interrupt Python code that is currently waiting on a timer or IO from what I can see). Also none of the functions we call here is marked as safe to be called from a signal handler from what I can see, so we might still end up crashing here with some bad timing. Python 3.2 introduced PyErr_SetInterrupt to solve this and the function takes care of all the details and avoids doing anything that isn't safe to do inside a signal handler. The only thing we need to do is to manually setup our own fake SIGINT handler that behaves the same way as the standalone Python REPL signal handler (which raises a KeyboardInterrupt). From what I understand the old code used to work with Python 2 so I kept the old code around until we officially drop support for Python 2. There is a small gap here with Python 3.0->3.1 where we might still be crashing, but those versions have reached their EOL more than a decade ago so I think we don't need to bother about them. Differential revision: https://reviews.llvm.org/D104886
2022-01-13 10:26:20 -09:00
#if LLDB_USE_PYTHON_SET_INTERRUPT
// If the interpreter isn't evaluating any Python at the moment then return
// false to signal that this function didn't handle the interrupt and the
// next component should try handling it.
if (!IsExecutingPython())
return false;
// Tell Python that it should pretend to have received a SIGINT.
PyErr_SetInterrupt();
// PyErr_SetInterrupt has no way to return an error so we can only pretend the
// signal got successfully handled and return true.
// Python 3.10 introduces PyErr_SetInterruptEx that could return an error, but
// the error handling is limited to checking the arguments which would be
// just our (hardcoded) input signal code SIGINT, so that's not useful at all.
return true;
#else
Log *log = GetLog(LLDBLog::Script);
if (IsExecutingPython()) {
PyThreadState *state = PyThreadState_Get();
if (!state)
state = GetThreadState();
if (state) {
long tid = PyThread_get_thread_ident();
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
PyThreadState_Swap(state);
int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt);
LLDB_LOGF(log,
"ScriptInterpreterPythonImpl::Interrupt() sending "
"PyExc_KeyboardInterrupt (tid = %li, num_threads = %i)...",
tid, num_threads);
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
return true;
}
}
LLDB_LOGF(log,
"ScriptInterpreterPythonImpl::Interrupt() python code not running, "
"can't interrupt");
return false;
[lldb] Fix that the embedded Python REPL crashes if it receives SIGINT When LLDB receives a SIGINT while running the embedded Python REPL it currently just crashes in ScriptInterpreterPythonImpl::Interrupt with an error such as the one below: Fatal Python error: PyThreadState_Get: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) The faulty code that causes this error is this part of ScriptInterpreterPythonImpl::Interrupt: PyThreadState *state = PyThreadState_GET(); if (!state) state = GetThreadState(); if (state) { long tid = state->thread_id; PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); The obvious fix I tried is to just acquire the GIL before this code is running which fixes the crash but the KeyboardInterrupt we want to raise immediately is actually just queued and would only be raised once the next line of input has been parsed (which e.g. won't interrupt Python code that is currently waiting on a timer or IO from what I can see). Also none of the functions we call here is marked as safe to be called from a signal handler from what I can see, so we might still end up crashing here with some bad timing. Python 3.2 introduced PyErr_SetInterrupt to solve this and the function takes care of all the details and avoids doing anything that isn't safe to do inside a signal handler. The only thing we need to do is to manually setup our own fake SIGINT handler that behaves the same way as the standalone Python REPL signal handler (which raises a KeyboardInterrupt). From what I understand the old code used to work with Python 2 so I kept the old code around until we officially drop support for Python 2. There is a small gap here with Python 3.0->3.1 where we might still be crashing, but those versions have reached their EOL more than a decade ago so I think we don't need to bother about them. Differential revision: https://reviews.llvm.org/D104886
2022-01-13 10:26:20 -09:00
#endif
}
bool ScriptInterpreterPythonImpl::ExecuteOneLineWithReturn(
llvm::StringRef in_string, ScriptInterpreter::ScriptReturnType return_type,
void *ret_value, const ExecuteScriptOptions &options) {
llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
io_redirect_or_error = ScriptInterpreterIORedirect::Create(
options.GetEnableIO(), m_debugger, /*result=*/nullptr);
if (!io_redirect_or_error) {
llvm::consumeError(io_redirect_or_error.takeError());
return false;
}
ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error;
Locker locker(this,
Locker::AcquireLock | Locker::InitSession |
(options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
Locker::NoSTDIN,
Locker::FreeAcquiredLock | Locker::TearDownSession,
io_redirect.GetInputFile(), io_redirect.GetOutputFile(),
io_redirect.GetErrorFile());
PythonModule &main_module = GetMainModule();
PythonDictionary globals = main_module.GetDictionary();
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
PythonDictionary locals = GetSessionDictionary();
if (!locals.IsValid())
locals = unwrapIgnoringErrors(
As<PythonDictionary>(globals.GetAttribute(m_dictionary_name)));
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (!locals.IsValid())
locals = globals;
Expected<PythonObject> maybe_py_return =
runStringOneLine(in_string, globals, locals);
if (!maybe_py_return) {
llvm::handleAllErrors(
maybe_py_return.takeError(),
[&](PythonException &E) {
E.Restore();
if (options.GetMaskoutErrors()) {
if (E.Matches(PyExc_SyntaxError)) {
PyErr_Print();
}
PyErr_Clear();
}
},
[](const llvm::ErrorInfoBase &E) {});
return false;
}
PythonObject py_return = std::move(maybe_py_return.get());
assert(py_return.IsValid());
switch (return_type) {
case eScriptReturnTypeCharPtr: // "char *"
{
const char format[3] = "s#";
return PyArg_Parse(py_return.get(), format, (char **)ret_value);
}
case eScriptReturnTypeCharStrOrNone: // char* or NULL if py_return ==
// Py_None
{
const char format[3] = "z";
return PyArg_Parse(py_return.get(), format, (char **)ret_value);
}
case eScriptReturnTypeBool: {
const char format[2] = "b";
return PyArg_Parse(py_return.get(), format, (bool *)ret_value);
}
case eScriptReturnTypeShortInt: {
const char format[2] = "h";
return PyArg_Parse(py_return.get(), format, (short *)ret_value);
}
case eScriptReturnTypeShortIntUnsigned: {
const char format[2] = "H";
return PyArg_Parse(py_return.get(), format, (unsigned short *)ret_value);
}
case eScriptReturnTypeInt: {
const char format[2] = "i";
return PyArg_Parse(py_return.get(), format, (int *)ret_value);
}
case eScriptReturnTypeIntUnsigned: {
const char format[2] = "I";
return PyArg_Parse(py_return.get(), format, (unsigned int *)ret_value);
}
case eScriptReturnTypeLongInt: {
const char format[2] = "l";
return PyArg_Parse(py_return.get(), format, (long *)ret_value);
}
case eScriptReturnTypeLongIntUnsigned: {
const char format[2] = "k";
return PyArg_Parse(py_return.get(), format, (unsigned long *)ret_value);
}
case eScriptReturnTypeLongLong: {
const char format[2] = "L";
return PyArg_Parse(py_return.get(), format, (long long *)ret_value);
}
case eScriptReturnTypeLongLongUnsigned: {
const char format[2] = "K";
return PyArg_Parse(py_return.get(), format,
(unsigned long long *)ret_value);
}
case eScriptReturnTypeFloat: {
const char format[2] = "f";
return PyArg_Parse(py_return.get(), format, (float *)ret_value);
}
case eScriptReturnTypeDouble: {
const char format[2] = "d";
return PyArg_Parse(py_return.get(), format, (double *)ret_value);
}
case eScriptReturnTypeChar: {
const char format[2] = "c";
return PyArg_Parse(py_return.get(), format, (char *)ret_value);
}
case eScriptReturnTypeOpaqueObject: {
*((PyObject **)ret_value) = py_return.release();
return true;
}
}
llvm_unreachable("Fully covered switch!");
}
Status ScriptInterpreterPythonImpl::ExecuteMultipleLines(
const char *in_string, const ExecuteScriptOptions &options) {
if (in_string == nullptr)
return Status();
llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
io_redirect_or_error = ScriptInterpreterIORedirect::Create(
options.GetEnableIO(), m_debugger, /*result=*/nullptr);
if (!io_redirect_or_error)
return Status::FromError(io_redirect_or_error.takeError());
ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error;
Locker locker(this,
Locker::AcquireLock | Locker::InitSession |
(options.GetSetLLDBGlobals() ? Locker::InitGlobals : 0) |
Locker::NoSTDIN,
Locker::FreeAcquiredLock | Locker::TearDownSession,
io_redirect.GetInputFile(), io_redirect.GetOutputFile(),
io_redirect.GetErrorFile());
PythonModule &main_module = GetMainModule();
PythonDictionary globals = main_module.GetDictionary();
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
PythonDictionary locals = GetSessionDictionary();
if (!locals.IsValid())
locals = unwrapIgnoringErrors(
As<PythonDictionary>(globals.GetAttribute(m_dictionary_name)));
if (!locals.IsValid())
locals = globals;
Expected<PythonObject> return_value =
runStringMultiLine(in_string, globals, locals);
if (!return_value) {
llvm::Error error =
llvm::handleErrors(return_value.takeError(), [&](PythonException &E) {
llvm::Error error = llvm::createStringError(
llvm::inconvertibleErrorCode(), E.ReadBacktrace());
if (!options.GetMaskoutErrors())
E.Restore();
return error;
});
return Status::FromError(std::move(error));
}
return Status();
}
void ScriptInterpreterPythonImpl::CollectDataForBreakpointCommandCallback(
std::vector<std::reference_wrapper<BreakpointOptions>> &bp_options_vec,
CommandReturnObject &result) {
m_active_io_handler = eIOHandlerBreakpoint;
m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler(
" ", *this, &bp_options_vec);
}
void ScriptInterpreterPythonImpl::CollectDataForWatchpointCommandCallback(
WatchpointOptions *wp_options, CommandReturnObject &result) {
m_active_io_handler = eIOHandlerWatchpoint;
m_debugger.GetCommandInterpreter().GetPythonCommandsFromIOHandler(
" ", *this, wp_options);
}
Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallbackFunction(
BreakpointOptions &bp_options, const char *function_name,
StructuredData::ObjectSP extra_args_sp) {
Status error;
// For now just cons up a oneliner that calls the provided function.
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
std::string function_signature = function_name;
llvm::Expected<unsigned> maybe_args =
GetMaxPositionalArgumentsForCallable(function_name);
if (!maybe_args) {
error = Status::FromErrorStringWithFormat(
"could not get num args: %s",
llvm::toString(maybe_args.takeError()).c_str());
return error;
}
size_t max_args = *maybe_args;
bool uses_extra_args = false;
if (max_args >= 4) {
uses_extra_args = true;
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
function_signature += "(frame, bp_loc, extra_args, internal_dict)";
} else if (max_args >= 3) {
if (extra_args_sp) {
error = Status::FromErrorStringWithFormat(
"cannot pass extra_args to a three argument callback");
return error;
}
uses_extra_args = false;
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
function_signature += "(frame, bp_loc, internal_dict)";
} else {
error = Status::FromErrorStringWithFormat("expected 3 or 4 argument "
"function, %s can only take %zu",
function_name, max_args);
return error;
}
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
SetBreakpointCommandCallback(bp_options, function_signature.c_str(),
extra_args_sp, uses_extra_args,
/*is_callback=*/true);
return error;
}
Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
BreakpointOptions &bp_options,
std::unique_ptr<BreakpointOptions::CommandData> &cmd_data_up) {
Status error;
error = GenerateBreakpointCommandCallbackData(cmd_data_up->user_source,
cmd_data_up->script_source,
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
/*has_extra_args=*/false,
/*is_callback=*/false);
if (error.Fail()) {
return error;
}
auto baton_sp =
std::make_shared<BreakpointOptions::CommandBaton>(std::move(cmd_data_up));
bp_options.SetCallback(
ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
return error;
}
Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
BreakpointOptions &bp_options, const char *command_body_text,
bool is_callback) {
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
return SetBreakpointCommandCallback(bp_options, command_body_text, {},
/*uses_extra_args=*/false, is_callback);
}
// Set a Python one-liner as the callback for the breakpoint.
Status ScriptInterpreterPythonImpl::SetBreakpointCommandCallback(
BreakpointOptions &bp_options, const char *command_body_text,
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
StructuredData::ObjectSP extra_args_sp, bool uses_extra_args,
bool is_callback) {
auto data_up = std::make_unique<CommandDataPython>(extra_args_sp);
// Split the command_body_text into lines, and pass that to
// GenerateBreakpointCommandCallbackData. That will wrap the body in an
// auto-generated function, and return the function name in script_source.
// That is what the callback will actually invoke.
data_up->user_source.SplitIntoLines(command_body_text);
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
Status error = GenerateBreakpointCommandCallbackData(
data_up->user_source, data_up->script_source, uses_extra_args,
is_callback);
if (error.Success()) {
auto baton_sp =
std::make_shared<BreakpointOptions::CommandBaton>(std::move(data_up));
bp_options.SetCallback(
ScriptInterpreterPythonImpl::BreakpointCallbackFunction, baton_sp);
return error;
}
return error;
}
// Set a Python one-liner as the callback for the watchpoint.
void ScriptInterpreterPythonImpl::SetWatchpointCommandCallback(
WatchpointOptions *wp_options, const char *user_input, bool is_callback) {
auto data_up = std::make_unique<WatchpointOptions::CommandData>();
// It's necessary to set both user_source and script_source to the oneliner.
// The former is used to generate callback description (as in watchpoint
// command list) while the latter is used for Python to interpret during the
// actual callback.
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
data_up->user_source.AppendString(user_input);
data_up->script_source.assign(user_input);
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
if (GenerateWatchpointCommandCallbackData(
data_up->user_source, data_up->script_source, is_callback)) {
auto baton_sp =
std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up));
wp_options->SetCallback(
ScriptInterpreterPythonImpl::WatchpointCallbackFunction, baton_sp);
}
}
Status ScriptInterpreterPythonImpl::ExportFunctionDefinitionToInterpreter(
StringList &function_def) {
// Convert StringList to one long, newline delimited, const char *.
std::string function_def_string(function_def.CopyList());
LLDB_LOG(GetLog(LLDBLog::Script), "Added Function:\n{0}\n",
function_def_string.c_str());
Status error = ExecuteMultipleLines(
function_def_string.c_str(), ExecuteScriptOptions().SetEnableIO(false));
return error;
}
Status ScriptInterpreterPythonImpl::GenerateFunction(const char *signature,
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
const StringList &input,
bool is_callback) {
Status error;
int num_lines = input.GetSize();
if (num_lines == 0) {
error = Status::FromErrorString("No input data.");
return error;
}
if (!signature || *signature == 0) {
error = Status::FromErrorString("No output function name.");
return error;
}
StreamString sstr;
StringList auto_generated_function;
auto_generated_function.AppendString(signature);
auto_generated_function.AppendString(
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
" global_dict = globals()"); // Grab the global dictionary
auto_generated_function.AppendString(
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
" new_keys = internal_dict.keys()"); // Make a list of keys in the
// session dict
auto_generated_function.AppendString(
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
" old_keys = global_dict.keys()"); // Save list of keys in global dict
auto_generated_function.AppendString(
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
" global_dict.update(internal_dict)"); // Add the session dictionary
// to the global dictionary.
if (is_callback) {
// If the user input is a callback to a python function, make sure the input
// is only 1 line, otherwise appending the user input would break the
// generated wrapped function
if (num_lines == 1) {
sstr.Clear();
sstr.Printf(" __return_val = %s", input.GetStringAtIndex(0));
auto_generated_function.AppendString(sstr.GetData());
} else {
return Status::FromErrorString(
"ScriptInterpreterPythonImpl::GenerateFunction(is_callback="
"true) = ERROR: python function is multiline.");
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
}
} else {
auto_generated_function.AppendString(
" __return_val = None"); // Initialize user callback return value.
auto_generated_function.AppendString(
" def __user_code():"); // Create a nested function that will wrap
// the user input. This is necessary to
// capture the return value of the user input
// and prevent early returns.
for (int i = 0; i < num_lines; ++i) {
sstr.Clear();
sstr.Printf(" %s", input.GetStringAtIndex(i));
auto_generated_function.AppendString(sstr.GetData());
}
auto_generated_function.AppendString(
" __return_val = __user_code()"); // Call user code and capture
// return value
}
auto_generated_function.AppendString(
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
" for key in new_keys:"); // Iterate over all the keys from session
// dict
auto_generated_function.AppendString(
" if key in old_keys:"); // If key was originally in
// global dict
auto_generated_function.AppendString(
" internal_dict[key] = global_dict[key]"); // Update it
auto_generated_function.AppendString(
" elif key in global_dict:"); // Then if it is still in the
// global dict
auto_generated_function.AppendString(
" del global_dict[key]"); // remove key/value from the
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
// global dict
auto_generated_function.AppendString(
" return __return_val"); // Return the user callback return value.
// Verify that the results are valid Python.
error = ExportFunctionDefinitionToInterpreter(auto_generated_function);
return error;
}
bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction(
StringList &user_input, std::string &output, const void *name_token) {
static uint32_t num_created_functions = 0;
user_input.RemoveBlankLines();
StreamString sstr;
// Check to see if we have any data; if not, just return.
if (user_input.GetSize() == 0)
return false;
// Take what the user wrote, wrap it all up inside one big auto-generated
// Python function, passing in the ValueObject as parameter to the function.
std::string auto_generated_function_name(
GenerateUniqueName("lldb_autogen_python_type_print_func",
num_created_functions, name_token));
sstr.Printf("def %s (valobj, internal_dict):",
auto_generated_function_name.c_str());
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
if (!GenerateFunction(sstr.GetData(), user_input, /*is_callback=*/false)
.Success())
return false;
// Store the name of the auto-generated function to be called.
output.assign(auto_generated_function_name);
return true;
}
bool ScriptInterpreterPythonImpl::GenerateScriptAliasFunction(
StringList &user_input, std::string &output) {
static uint32_t num_created_functions = 0;
user_input.RemoveBlankLines();
StreamString sstr;
// Check to see if we have any data; if not, just return.
if (user_input.GetSize() == 0)
return false;
std::string auto_generated_function_name(GenerateUniqueName(
"lldb_autogen_python_cmd_alias_func", num_created_functions));
sstr.Printf("def %s (debugger, args, exe_ctx, result, internal_dict):",
auto_generated_function_name.c_str());
if (!GenerateFunction(sstr.GetData(), user_input, /*is_callback=*/false)
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
.Success())
return false;
// Store the name of the auto-generated function to be called.
output.assign(auto_generated_function_name);
return true;
}
bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass(
StringList &user_input, std::string &output, const void *name_token) {
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
static uint32_t num_created_classes = 0;
user_input.RemoveBlankLines();
int num_lines = user_input.GetSize();
StreamString sstr;
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
// Check to see if we have any data; if not, just return.
if (user_input.GetSize() == 0)
return false;
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
// Wrap all user input into a Python class
std::string auto_generated_class_name(GenerateUniqueName(
"lldb_autogen_python_type_synth_class", num_created_classes, name_token));
StringList auto_generated_class;
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
// Create the function name & definition string.
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
sstr.Printf("class %s:", auto_generated_class_name.c_str());
auto_generated_class.AppendString(sstr.GetString());
// Wrap everything up inside the class, increasing the indentation. we don't
// need to play any fancy indentation tricks here because there is no
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
// surrounding code whose indentation we need to honor
for (int i = 0; i < num_lines; ++i) {
sstr.Clear();
sstr.Printf(" %s", user_input.GetStringAtIndex(i));
auto_generated_class.AppendString(sstr.GetString());
}
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
// Verify that the results are valid Python. (even though the method is
// ExportFunctionDefinitionToInterpreter, a class will actually be exported)
// (TODO: rename that method to ExportDefinitionToInterpreter)
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
if (!ExportFunctionDefinitionToInterpreter(auto_generated_class).Success())
return false;
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
// Store the name of the auto-generated class
output.assign(auto_generated_class_name);
return true;
}
ScriptedProcessInterfaceUP
ScriptInterpreterPythonImpl::CreateScriptedProcessInterface() {
return std::make_unique<ScriptedProcessPythonInterface>(*this);
}
ScriptedHookInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedHookInterface() {
return std::make_shared<ScriptedHookPythonInterface>(*this);
}
ScriptedBreakpointInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedBreakpointInterface() {
return std::make_shared<ScriptedBreakpointPythonInterface>(*this);
}
ScriptedStackFrameRecognizerInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedStackFrameRecognizerInterface() {
return std::make_shared<ScriptedStackFrameRecognizerPythonInterface>(*this);
}
ScriptedCommandInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedCommandInterface() {
return std::make_shared<ScriptedCommandPythonInterface>(*this);
}
ScriptedStringSummaryInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedStringSummaryInterface() {
return std::make_shared<ScriptedStringSummaryPythonInterface>(*this);
}
ScriptedSyntheticChildrenInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedSyntheticChildrenInterface() {
return std::make_shared<ScriptedSyntheticChildrenPythonInterface>(*this);
}
ScriptedThreadInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedThreadInterface() {
return std::make_shared<ScriptedThreadPythonInterface>(*this);
}
ScriptedFrameInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedFrameInterface() {
return std::make_shared<ScriptedFramePythonInterface>(*this);
}
ScriptedFrameProviderInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedFrameProviderInterface() {
return std::make_shared<ScriptedFrameProviderPythonInterface>(*this);
}
ScriptedThreadPlanInterfaceSP
ScriptInterpreterPythonImpl::CreateScriptedThreadPlanInterface() {
return std::make_shared<ScriptedThreadPlanPythonInterface>(*this);
}
OperatingSystemInterfaceSP
ScriptInterpreterPythonImpl::CreateOperatingSystemInterface() {
return std::make_shared<OperatingSystemPythonInterface>(*this);
}
StructuredData::ObjectSP
ScriptInterpreterPythonImpl::CreateStructuredDataFromScriptObject(
ScriptObject obj) {
void *ptr = const_cast<void *>(obj.GetPointer());
Locker py_lock(this, Locker::AcquireLock | Locker::NoSTDIN, Locker::FreeLock);
PythonObject py_obj(PyRefType::Borrowed, static_cast<PyObject *>(ptr));
if (!py_obj.IsValid() || py_obj.IsNone())
return {};
return py_obj.CreateStructuredObject();
}
StructuredData::ObjectSP
ScriptInterpreterPythonImpl::LoadPluginModule(const FileSpec &file_spec,
lldb_private::Status &error) {
if (!FileSystem::Instance().Exists(file_spec)) {
error = Status::FromErrorString("no such file");
return StructuredData::ObjectSP();
}
StructuredData::ObjectSP module_sp;
LoadScriptOptions load_script_options =
LoadScriptOptions().SetInitSession(true).SetSilent(false);
if (LoadScriptingModule(file_spec.GetPath().c_str(), load_script_options,
error, &module_sp))
return module_sp;
return StructuredData::ObjectSP();
}
StructuredData::DictionarySP ScriptInterpreterPythonImpl::GetDynamicSettings(
StructuredData::ObjectSP plugin_module_sp, Target *target,
const char *setting_name, lldb_private::Status &error) {
if (!plugin_module_sp || !target || !setting_name || !setting_name[0])
return StructuredData::DictionarySP();
StructuredData::Generic *generic = plugin_module_sp->GetAsGeneric();
if (!generic)
return StructuredData::DictionarySP();
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
TargetSP target_sp(target->shared_from_this());
auto setting = (PyObject *)SWIGBridge::LLDBSWIGPython_GetDynamicSetting(
generic->GetValue(), setting_name, target_sp);
if (!setting)
return StructuredData::DictionarySP();
PythonDictionary py_dict =
unwrapIgnoringErrors(As<PythonDictionary>(Take<PythonObject>(setting)));
if (!py_dict)
return StructuredData::DictionarySP();
return py_dict.CreateStructuredDictionary();
}
bool ScriptInterpreterPythonImpl::GenerateTypeScriptFunction(
const char *oneliner, std::string &output, const void *name_token) {
StringList input;
input.SplitIntoLines(oneliner, strlen(oneliner));
return GenerateTypeScriptFunction(input, output, name_token);
}
bool ScriptInterpreterPythonImpl::GenerateTypeSynthClass(
const char *oneliner, std::string &output, const void *name_token) {
StringList input;
input.SplitIntoLines(oneliner, strlen(oneliner));
return GenerateTypeSynthClass(input, output, name_token);
}
Status ScriptInterpreterPythonImpl::GenerateBreakpointCommandCallbackData(
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
StringList &user_input, std::string &output, bool has_extra_args,
bool is_callback) {
static uint32_t num_created_functions = 0;
user_input.RemoveBlankLines();
StreamString sstr;
Status error;
if (user_input.GetSize() == 0) {
error = Status::FromErrorString("No input data.");
return error;
}
std::string auto_generated_function_name(GenerateUniqueName(
"lldb_autogen_python_bp_callback_func_", num_created_functions));
if (has_extra_args)
sstr.Printf("def %s (frame, bp_loc, extra_args, internal_dict):",
auto_generated_function_name.c_str());
else
sstr.Printf("def %s (frame, bp_loc, internal_dict):",
auto_generated_function_name.c_str());
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
error = GenerateFunction(sstr.GetData(), user_input, is_callback);
if (!error.Success())
return error;
// Store the name of the auto-generated function to be called.
output.assign(auto_generated_function_name);
return error;
}
bool ScriptInterpreterPythonImpl::GenerateWatchpointCommandCallbackData(
StringList &user_input, std::string &output, bool is_callback) {
static uint32_t num_created_functions = 0;
user_input.RemoveBlankLines();
StreamString sstr;
if (user_input.GetSize() == 0)
return false;
std::string auto_generated_function_name(GenerateUniqueName(
"lldb_autogen_python_wp_callback_func_", num_created_functions));
sstr.Printf("def %s (frame, wp, internal_dict):",
auto_generated_function_name.c_str());
[lldb] Fix {break,watch}point command function stopping behaviour In order to run a {break,watch}point command, lldb can resolve to the script interpreter to run an arbitrary piece of code or call into a user-provided function. To do so, we will generate a wrapping function, where we first copy lldb's internal dictionary keys into the interpreter's global dictionary, copied inline the user code before resetting the global dictionary to its previous state. However, {break,watch}point commands can optionally return a value that would tell lldb whether we should stop or not. This feature was only implemented for breakpoint commands and since we inlined the user code directly into the wrapping function, introducing an early return, that caused lldb to let the interpreter global dictionary tinted with the internal dictionary keys. This patch fixes that issue while also adding the stopping behaviour to watchpoint commands. To do so, this patch refactors the {break,watch}point command creation method, to let the lldb wrapper function generator know if the user code is a function call or a arbitrary expression. Then the wrapper generator, if the user input was a function call, the wrapper function will call the user function and save the return value into a variable. If the user input was an arbitrary expression, the wrapper will inline it into a nested function, call the nested function and save the return value into the same variable. After resetting the interpreter global dictionary to its previous state, the generated wrapper function will return the varible containing the return value. rdar://105461140 Differential Revision: https://reviews.llvm.org/D144688 Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
2023-02-28 08:24:46 -09:00
if (!GenerateFunction(sstr.GetData(), user_input, is_callback).Success())
return false;
// Store the name of the auto-generated function to be called.
output.assign(auto_generated_function_name);
return true;
}
bool ScriptInterpreterPythonImpl::GetScriptedSummary(
const char *python_function_name, lldb::ValueObjectSP valobj,
StructuredData::ObjectSP &callee_wrapper_sp,
const TypeSummaryOptions &options, std::string &retval) {
LLDB_SCOPED_TIMER();
if (!valobj.get()) {
retval.assign("<no object>");
return false;
}
void *old_callee = nullptr;
StructuredData::Generic *generic = nullptr;
if (callee_wrapper_sp) {
generic = callee_wrapper_sp->GetAsGeneric();
if (generic)
old_callee = generic->GetValue();
}
void *new_callee = old_callee;
bool ret_val;
if (python_function_name && *python_function_name) {
{
Locker py_lock(this, Locker::AcquireLock | Locker::InitSession |
Locker::NoSTDIN);
{
TypeSummaryOptionsSP options_sp(new TypeSummaryOptions(options));
static Timer::Category func_cat("LLDBSwigPythonCallTypeScript");
Timer scoped_timer(func_cat, "LLDBSwigPythonCallTypeScript");
ret_val = SWIGBridge::LLDBSwigPythonCallTypeScript(
Fix ref counting of Python objects. PythonObjects were being incorrectly ref-counted. This problem was pervasive throughout the codebase, leading to an unknown number of memory leaks and potentially use-after-free. The issue stems from the fact that Python native methods can either return "borrowed" references or "owned" references. For the former category, you *must* incref it prior to decrefing it. And for the latter category, you should not incref it before decrefing it. This is mostly an issue when a Python C API method returns a `PyObject` to you, but it can also happen with a method accepts a `PyObject`. Notably, this happens in `PyList_SetItem`, which is documented to "steal" the reference that you give it. So if you pass something to `PyList_SetItem`, you cannot hold onto it unless you incref it first. But since this is one of only two exceptions in the entire API, it's confusing and difficult to remember. Our `PythonObject` class was indiscriminantely increfing every object it received, which means that if you passed it an owned reference, you now have a dangling reference since owned references should not be increfed. We were doing this in quite a few places. There was also a fair amount of manual increfing and decrefing prevalent throughout the codebase, which is easy to get wrong. This patch solves the problem by making any construction of a `PythonObject` from a `PyObject` take a flag which indicates whether it is an owned reference or a borrowed reference. There is no way to construct a `PythonObject` without this flag, and it does not offer a default value, forcing the user to make an explicit decision every time. All manual uses of `PyObject` have been cleaned up throughout the codebase and replaced with `PythonObject` in order to make RAII the predominant pattern when dealing with native Python objects. Differential Revision: http://reviews.llvm.org/D13617 Reviewed By: Greg Clayton llvm-svn: 250195
2015-10-13 10:16:15 -08:00
python_function_name, GetSessionDictionary().get(), valobj,
&new_callee, options_sp, retval);
}
}
} else {
retval.assign("<no function name>");
return false;
}
if (new_callee && old_callee != new_callee) {
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
callee_wrapper_sp = std::make_shared<StructuredPythonObject>(
PythonObject(PyRefType::Borrowed, static_cast<PyObject *>(new_callee)));
}
return ret_val;
}
bool ScriptInterpreterPythonImpl::FormatterCallbackFunction(
const char *python_function_name, TypeImplSP type_impl_sp) {
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
return SWIGBridge::LLDBSwigPythonFormatterCallbackFunction(
python_function_name, m_dictionary_name.c_str(), type_impl_sp);
}
bool ScriptInterpreterPythonImpl::BreakpointCallbackFunction(
void *baton, StoppointCallbackContext *context, user_id_t break_id,
user_id_t break_loc_id) {
CommandDataPython *bp_option_data = (CommandDataPython *)baton;
const char *python_function_name = bp_option_data->script_source.c_str();
if (!context)
return true;
ExecutionContext exe_ctx(context->exe_ctx_ref);
Target *target = exe_ctx.GetTargetPtr();
if (!target)
return true;
Debugger &debugger = target->GetDebugger();
ScriptInterpreterPythonImpl *python_interpreter =
GetPythonInterpreter(debugger);
if (!python_interpreter)
return true;
if (python_function_name && python_function_name[0]) {
const StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
BreakpointSP breakpoint_sp = target->GetBreakpointByID(break_id);
if (breakpoint_sp) {
const BreakpointLocationSP bp_loc_sp(
Redesign of the interaction between Python and frozen objects: - introduced two new classes ValueObjectConstResultChild and ValueObjectConstResultImpl: the first one is a ValueObjectChild obtained from a ValueObjectConstResult, the second is a common implementation backend for VOCR and VOCRCh of method calls meant to read through pointers stored in frozen objects ; now such reads transparently move from host to target as required - as a consequence of the above, removed code that made target-memory copies of expression results in several places throughout LLDB, and also removed code that enabled to recognize an expression result VO as such - introduced a new GetPointeeData() method in ValueObject that lets you read a given amount of objects of type T from a VO representing a T* or T[], and doing dereferences transparently in private layer it returns a DataExtractor ; in public layer it returns an instance of a newly created lldb::SBData - as GetPointeeData() does the right thing for both frozen and non-frozen ValueObject's, reimplemented ReadPointedString() to use it en lieu of doing the raw read itself - introduced a new GetData() method in ValueObject that lets you get a copy of the data that backs the ValueObject (for pointers, this returns the address without any previous dereferencing steps ; for arrays it actually reads the whole chunk of memory) in public layer this returns an SBData, just like GetPointeeData() - introduced a new CreateValueFromData() method in SBValue that lets you create a new SBValue from a chunk of data wrapped in an SBData the limitation to remember for this kind of SBValue is that they have no address: extracting the address-of for these objects (with any of GetAddress(), GetLoadAddress() and AddressOf()) will return invalid values - added several tests to check that "p"-ing objects (STL classes, char* and char[]) will do the right thing Solved a bug where global pointers to global variables were not dereferenced correctly for display New target setting "max-string-summary-length" gives the maximum number of characters to show in a string when summarizing it, instead of the hardcoded 128 Solved a bug where the summary for char[] and char* would not be shown if the ValueObject's were dumped via the "p" command Removed m_pointers_point_to_load_addrs from ValueObject. Introduced a new m_address_type_of_children, which each ValueObject can set to tell the address type of any pointers and/or references it creates. In the current codebase, this is load address most of the time (the only notable exception being file addresses that generate file address children UNLESS we have a live process) Updated help text for summary-string Fixed an issue in STL formatters where std::stlcontainer::iterator would match the container's synthetic children providers Edited the syntax and help for some commands to have proper argument types llvm-svn: 139160
2011-09-06 11:20:51 -08:00
breakpoint_sp->FindLocationByID(break_loc_id));
if (stop_frame_sp && bp_loc_sp) {
bool ret_val = true;
Redesign of the interaction between Python and frozen objects: - introduced two new classes ValueObjectConstResultChild and ValueObjectConstResultImpl: the first one is a ValueObjectChild obtained from a ValueObjectConstResult, the second is a common implementation backend for VOCR and VOCRCh of method calls meant to read through pointers stored in frozen objects ; now such reads transparently move from host to target as required - as a consequence of the above, removed code that made target-memory copies of expression results in several places throughout LLDB, and also removed code that enabled to recognize an expression result VO as such - introduced a new GetPointeeData() method in ValueObject that lets you read a given amount of objects of type T from a VO representing a T* or T[], and doing dereferences transparently in private layer it returns a DataExtractor ; in public layer it returns an instance of a newly created lldb::SBData - as GetPointeeData() does the right thing for both frozen and non-frozen ValueObject's, reimplemented ReadPointedString() to use it en lieu of doing the raw read itself - introduced a new GetData() method in ValueObject that lets you get a copy of the data that backs the ValueObject (for pointers, this returns the address without any previous dereferencing steps ; for arrays it actually reads the whole chunk of memory) in public layer this returns an SBData, just like GetPointeeData() - introduced a new CreateValueFromData() method in SBValue that lets you create a new SBValue from a chunk of data wrapped in an SBData the limitation to remember for this kind of SBValue is that they have no address: extracting the address-of for these objects (with any of GetAddress(), GetLoadAddress() and AddressOf()) will return invalid values - added several tests to check that "p"-ing objects (STL classes, char* and char[]) will do the right thing Solved a bug where global pointers to global variables were not dereferenced correctly for display New target setting "max-string-summary-length" gives the maximum number of characters to show in a string when summarizing it, instead of the hardcoded 128 Solved a bug where the summary for char[] and char* would not be shown if the ValueObject's were dumped via the "p" command Removed m_pointers_point_to_load_addrs from ValueObject. Introduced a new m_address_type_of_children, which each ValueObject can set to tell the address type of any pointers and/or references it creates. In the current codebase, this is load address most of the time (the only notable exception being file addresses that generate file address children UNLESS we have a live process) Updated help text for summary-string Fixed an issue in STL formatters where std::stlcontainer::iterator would match the container's synthetic children providers Edited the syntax and help for some commands to have proper argument types llvm-svn: 139160
2011-09-06 11:20:51 -08:00
{
Locker py_lock(python_interpreter, Locker::AcquireLock |
Locker::InitSession |
Redesign of the interaction between Python and frozen objects: - introduced two new classes ValueObjectConstResultChild and ValueObjectConstResultImpl: the first one is a ValueObjectChild obtained from a ValueObjectConstResult, the second is a common implementation backend for VOCR and VOCRCh of method calls meant to read through pointers stored in frozen objects ; now such reads transparently move from host to target as required - as a consequence of the above, removed code that made target-memory copies of expression results in several places throughout LLDB, and also removed code that enabled to recognize an expression result VO as such - introduced a new GetPointeeData() method in ValueObject that lets you read a given amount of objects of type T from a VO representing a T* or T[], and doing dereferences transparently in private layer it returns a DataExtractor ; in public layer it returns an instance of a newly created lldb::SBData - as GetPointeeData() does the right thing for both frozen and non-frozen ValueObject's, reimplemented ReadPointedString() to use it en lieu of doing the raw read itself - introduced a new GetData() method in ValueObject that lets you get a copy of the data that backs the ValueObject (for pointers, this returns the address without any previous dereferencing steps ; for arrays it actually reads the whole chunk of memory) in public layer this returns an SBData, just like GetPointeeData() - introduced a new CreateValueFromData() method in SBValue that lets you create a new SBValue from a chunk of data wrapped in an SBData the limitation to remember for this kind of SBValue is that they have no address: extracting the address-of for these objects (with any of GetAddress(), GetLoadAddress() and AddressOf()) will return invalid values - added several tests to check that "p"-ing objects (STL classes, char* and char[]) will do the right thing Solved a bug where global pointers to global variables were not dereferenced correctly for display New target setting "max-string-summary-length" gives the maximum number of characters to show in a string when summarizing it, instead of the hardcoded 128 Solved a bug where the summary for char[] and char* would not be shown if the ValueObject's were dumped via the "p" command Removed m_pointers_point_to_load_addrs from ValueObject. Introduced a new m_address_type_of_children, which each ValueObject can set to tell the address type of any pointers and/or references it creates. In the current codebase, this is load address most of the time (the only notable exception being file addresses that generate file address children UNLESS we have a live process) Updated help text for summary-string Fixed an issue in STL formatters where std::stlcontainer::iterator would match the container's synthetic children providers Edited the syntax and help for some commands to have proper argument types llvm-svn: 139160
2011-09-06 11:20:51 -08:00
Locker::NoSTDIN);
Expected<bool> maybe_ret_val =
SWIGBridge::LLDBSwigPythonBreakpointCallbackFunction(
python_function_name,
python_interpreter->m_dictionary_name.c_str(), stop_frame_sp,
bp_loc_sp, bp_option_data->m_extra_args);
if (!maybe_ret_val) {
llvm::handleAllErrors(
maybe_ret_val.takeError(),
[&](PythonException &E) {
*debugger.GetAsyncErrorStream() << E.ReadBacktrace();
},
[&](const llvm::ErrorInfoBase &E) {
*debugger.GetAsyncErrorStream() << E.message();
});
} else {
ret_val = maybe_ret_val.get();
}
Redesign of the interaction between Python and frozen objects: - introduced two new classes ValueObjectConstResultChild and ValueObjectConstResultImpl: the first one is a ValueObjectChild obtained from a ValueObjectConstResult, the second is a common implementation backend for VOCR and VOCRCh of method calls meant to read through pointers stored in frozen objects ; now such reads transparently move from host to target as required - as a consequence of the above, removed code that made target-memory copies of expression results in several places throughout LLDB, and also removed code that enabled to recognize an expression result VO as such - introduced a new GetPointeeData() method in ValueObject that lets you read a given amount of objects of type T from a VO representing a T* or T[], and doing dereferences transparently in private layer it returns a DataExtractor ; in public layer it returns an instance of a newly created lldb::SBData - as GetPointeeData() does the right thing for both frozen and non-frozen ValueObject's, reimplemented ReadPointedString() to use it en lieu of doing the raw read itself - introduced a new GetData() method in ValueObject that lets you get a copy of the data that backs the ValueObject (for pointers, this returns the address without any previous dereferencing steps ; for arrays it actually reads the whole chunk of memory) in public layer this returns an SBData, just like GetPointeeData() - introduced a new CreateValueFromData() method in SBValue that lets you create a new SBValue from a chunk of data wrapped in an SBData the limitation to remember for this kind of SBValue is that they have no address: extracting the address-of for these objects (with any of GetAddress(), GetLoadAddress() and AddressOf()) will return invalid values - added several tests to check that "p"-ing objects (STL classes, char* and char[]) will do the right thing Solved a bug where global pointers to global variables were not dereferenced correctly for display New target setting "max-string-summary-length" gives the maximum number of characters to show in a string when summarizing it, instead of the hardcoded 128 Solved a bug where the summary for char[] and char* would not be shown if the ValueObject's were dumped via the "p" command Removed m_pointers_point_to_load_addrs from ValueObject. Introduced a new m_address_type_of_children, which each ValueObject can set to tell the address type of any pointers and/or references it creates. In the current codebase, this is load address most of the time (the only notable exception being file addresses that generate file address children UNLESS we have a live process) Updated help text for summary-string Fixed an issue in STL formatters where std::stlcontainer::iterator would match the container's synthetic children providers Edited the syntax and help for some commands to have proper argument types llvm-svn: 139160
2011-09-06 11:20:51 -08:00
}
return ret_val;
}
}
}
// We currently always true so we stop in case anything goes wrong when
// trying to call the script function
return true;
}
bool ScriptInterpreterPythonImpl::WatchpointCallbackFunction(
void *baton, StoppointCallbackContext *context, user_id_t watch_id) {
WatchpointOptions::CommandData *wp_option_data =
(WatchpointOptions::CommandData *)baton;
const char *python_function_name = wp_option_data->script_source.c_str();
if (!context)
return true;
ExecutionContext exe_ctx(context->exe_ctx_ref);
Target *target = exe_ctx.GetTargetPtr();
if (!target)
return true;
Debugger &debugger = target->GetDebugger();
ScriptInterpreterPythonImpl *python_interpreter =
GetPythonInterpreter(debugger);
if (!python_interpreter)
return true;
if (python_function_name && python_function_name[0]) {
const StackFrameSP stop_frame_sp(exe_ctx.GetFrameSP());
WatchpointSP wp_sp = target->GetWatchpointList().FindByID(watch_id);
if (wp_sp) {
if (stop_frame_sp && wp_sp) {
bool ret_val = true;
{
Locker py_lock(python_interpreter, Locker::AcquireLock |
Locker::InitSession |
Locker::NoSTDIN);
ret_val = SWIGBridge::LLDBSwigPythonWatchpointCallbackFunction(
python_function_name,
python_interpreter->m_dictionary_name.c_str(), stop_frame_sp,
wp_sp);
}
return ret_val;
}
}
}
// We currently always true so we stop in case anything goes wrong when
// trying to call the script function
return true;
}
bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
const char *impl_function, Process *process, std::string &output,
Status &error) {
bool ret_val;
if (!process) {
error = Status::FromErrorString("no process");
return false;
}
if (!impl_function || !impl_function[0]) {
error = Status::FromErrorString("no function to execute");
return false;
}
{
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
ret_val = SWIGBridge::LLDBSWIGPythonRunScriptKeywordProcess(
impl_function, m_dictionary_name.c_str(), process->shared_from_this(),
output);
if (!ret_val)
error = Status::FromErrorString("python script evaluation failed");
}
return ret_val;
}
bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
const char *impl_function, Thread *thread, std::string &output,
Status &error) {
if (!thread) {
error = Status::FromErrorString("no thread");
return false;
}
if (!impl_function || !impl_function[0]) {
error = Status::FromErrorString("no function to execute");
return false;
}
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
if (std::optional<std::string> result =
SWIGBridge::LLDBSWIGPythonRunScriptKeywordThread(
impl_function, m_dictionary_name.c_str(),
thread->shared_from_this())) {
output = std::move(*result);
return true;
}
error = Status::FromErrorString("python script evaluation failed");
return false;
}
bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
const char *impl_function, Target *target, std::string &output,
Status &error) {
bool ret_val;
if (!target) {
error = Status::FromErrorString("no thread");
return false;
}
if (!impl_function || !impl_function[0]) {
error = Status::FromErrorString("no function to execute");
return false;
}
{
TargetSP target_sp(target->shared_from_this());
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
ret_val = SWIGBridge::LLDBSWIGPythonRunScriptKeywordTarget(
impl_function, m_dictionary_name.c_str(), target_sp, output);
if (!ret_val)
error = Status::FromErrorString("python script evaluation failed");
}
return ret_val;
}
bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
const char *impl_function, StackFrame *frame, std::string &output,
Status &error) {
if (!frame) {
error = Status::FromErrorString("no frame");
return false;
}
if (!impl_function || !impl_function[0]) {
error = Status::FromErrorString("no function to execute");
return false;
}
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
if (std::optional<std::string> result =
SWIGBridge::LLDBSWIGPythonRunScriptKeywordFrame(
impl_function, m_dictionary_name.c_str(),
frame->shared_from_this())) {
output = std::move(*result);
return true;
}
error = Status::FromErrorString("python script evaluation failed");
return false;
}
bool ScriptInterpreterPythonImpl::RunScriptFormatKeyword(
const char *impl_function, ValueObject *value, std::string &output,
Status &error) {
bool ret_val;
if (!value) {
error = Status::FromErrorString("no value");
return false;
}
if (!impl_function || !impl_function[0]) {
error = Status::FromErrorString("no function to execute");
return false;
}
{
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN);
ret_val = SWIGBridge::LLDBSWIGPythonRunScriptKeywordValue(
impl_function, m_dictionary_name.c_str(), value->GetSP(), output);
if (!ret_val)
error = Status::FromErrorString("python script evaluation failed");
}
return ret_val;
}
uint64_t replace_all(std::string &str, const std::string &oldStr,
const std::string &newStr) {
size_t pos = 0;
uint64_t matches = 0;
while ((pos = str.find(oldStr, pos)) != std::string::npos) {
matches++;
str.replace(pos, oldStr.length(), newStr);
pos += newStr.length();
}
return matches;
}
bool ScriptInterpreterPythonImpl::LoadScriptingModule(
const char *pathname, const LoadScriptOptions &options,
lldb_private::Status &error, StructuredData::ObjectSP *module_sp,
FileSpec extra_search_dir, lldb::TargetSP target_sp) {
namespace fs = llvm::sys::fs;
namespace path = llvm::sys::path;
ExecuteScriptOptions exc_options = ExecuteScriptOptions()
.SetEnableIO(!options.GetSilent())
.SetSetLLDBGlobals(false);
if (!pathname || !pathname[0]) {
error = Status::FromErrorString("empty path");
return false;
}
llvm::Expected<std::unique_ptr<ScriptInterpreterIORedirect>>
io_redirect_or_error = ScriptInterpreterIORedirect::Create(
exc_options.GetEnableIO(), m_debugger, /*result=*/nullptr);
if (!io_redirect_or_error) {
error = Status::FromError(io_redirect_or_error.takeError());
return false;
}
ScriptInterpreterIORedirect &io_redirect = **io_redirect_or_error;
// Before executing Python code, lock the GIL.
Locker py_lock(this,
Locker::AcquireLock |
(options.GetInitSession() ? Locker::InitSession : 0) |
Locker::NoSTDIN,
Locker::FreeAcquiredLock |
(options.GetInitSession() ? Locker::TearDownSession : 0),
io_redirect.GetInputFile(), io_redirect.GetOutputFile(),
io_redirect.GetErrorFile());
auto ExtendSysPath = [&](std::string directory) -> llvm::Error {
if (directory.empty()) {
return llvm::createStringError("invalid directory name");
}
replace_all(directory, "\\", "\\\\");
replace_all(directory, "'", "\\'");
// Make sure that Python has "directory" in the search path.
StreamString command_stream;
command_stream.Printf("if not (sys.path.__contains__('%s')):\n "
"sys.path.insert(1,'%s');\n\n",
directory.c_str(), directory.c_str());
bool syspath_retval =
ExecuteMultipleLines(command_stream.GetData(), exc_options).Success();
if (!syspath_retval)
return llvm::createStringError("Python sys.path handling failed");
return llvm::Error::success();
};
std::string module_name(pathname);
bool possible_package = false;
if (extra_search_dir) {
if (llvm::Error e = ExtendSysPath(extra_search_dir.GetPath())) {
error = Status::FromError(std::move(e));
return false;
}
} else {
FileSpec module_file(pathname);
FileSystem::Instance().Resolve(module_file);
fs::file_status st;
std::error_code ec = status(module_file.GetPath(), st);
if (ec || st.type() == fs::file_type::status_error ||
st.type() == fs::file_type::type_unknown ||
st.type() == fs::file_type::file_not_found) {
// if not a valid file of any sort, check if it might be a filename still
// dot can't be used but / and \ can, and if either is found, reject
if (strchr(pathname, '\\') || strchr(pathname, '/')) {
error = Status::FromErrorStringWithFormatv("invalid pathname '{0}'",
pathname);
return false;
}
// Not a filename, probably a package of some sort, let it go through.
possible_package = true;
} else if (is_directory(st) || is_regular_file(st)) {
if (module_file.GetDirectory().empty()) {
error = Status::FromErrorStringWithFormatv(
"invalid directory name '{0}'", pathname);
return false;
}
if (llvm::Error e = ExtendSysPath(module_file.GetDirectory().str())) {
error = Status::FromError(std::move(e));
return false;
}
module_name = module_file.GetFilename().str();
} else {
error = Status::FromErrorString(
"no known way to import this module specification");
return false;
}
}
// Strip .py or .pyc extension
llvm::StringRef extension = llvm::sys::path::extension(module_name);
if (!extension.empty()) {
if (extension == ".py")
module_name.resize(module_name.length() - 3);
else if (extension == ".pyc")
module_name.resize(module_name.length() - 4);
}
if (!possible_package && module_name.find('.') != llvm::StringRef::npos) {
error = Status::FromErrorStringWithFormat(
"Python does not allow dots in module names: %s", module_name.c_str());
return false;
}
if (module_name.find('-') != llvm::StringRef::npos) {
error = Status::FromErrorStringWithFormat(
"Python discourages dashes in module names: %s", module_name.c_str());
return false;
}
// Check if the module is already imported.
StreamString command_stream;
command_stream.Clear();
command_stream.Printf("sys.modules.__contains__('%s')", module_name.c_str());
bool does_contain = false;
// This call will succeed if the module was ever imported in any Debugger in
// the lifetime of the process in which this LLDB framework is living.
const bool does_contain_executed = ExecuteOneLineWithReturn(
command_stream.GetData(),
ScriptInterpreterPythonImpl::eScriptReturnTypeBool, &does_contain,
exc_options);
const bool was_imported_globally = does_contain_executed && does_contain;
const bool was_imported_locally =
GetSessionDictionary()
.GetItemForKey(PythonString(module_name))
.IsAllocated();
// now actually do the import
command_stream.Clear();
if (was_imported_globally || was_imported_locally) {
if (!was_imported_locally)
command_stream.Printf("import %s ; reload_module(%s)",
module_name.c_str(), module_name.c_str());
else
command_stream.Printf("reload_module(%s)", module_name.c_str());
} else
command_stream.Printf("import %s", module_name.c_str());
error = ExecuteMultipleLines(command_stream.GetData(), exc_options);
if (error.Fail())
return false;
// if we are here, everything worked
// call __lldb_init_module(debugger,dict)
if (!SWIGBridge::LLDBSwigPythonCallModuleInit(
module_name.c_str(), m_dictionary_name.c_str(),
m_debugger.shared_from_this())) {
error = Status::FromErrorString("calling __lldb_init_module failed");
return false;
}
if (module_sp) {
// everything went just great, now set the module object
command_stream.Clear();
command_stream.Printf("%s", module_name.c_str());
void *module_pyobj = nullptr;
if (ExecuteOneLineWithReturn(
command_stream.GetData(),
ScriptInterpreter::eScriptReturnTypeOpaqueObject, &module_pyobj,
exc_options) &&
module_pyobj)
*module_sp = std::make_shared<StructuredPythonObject>(PythonObject(
PyRefType::Owned, static_cast<PyObject *>(module_pyobj)));
}
// Finally, if we got a target passed in, then we should tell the new module
// about this target:
if (target_sp)
return SWIGBridge::LLDBSwigPythonCallModuleNewTarget(
module_name.c_str(), m_dictionary_name.c_str(), target_sp);
return true;
}
bool ScriptInterpreterPythonImpl::IsReservedWord(const char *word) {
if (!word || !word[0])
return false;
llvm::StringRef word_sr(word);
// filter out a few characters that would just confuse us and that are
// clearly not keyword material anyway
if (word_sr.find('"') != llvm::StringRef::npos ||
word_sr.find('\'') != llvm::StringRef::npos)
return false;
StreamString command_stream;
command_stream.Printf("keyword.iskeyword('%s')", word);
bool result;
ExecuteScriptOptions options;
options.SetEnableIO(false);
options.SetMaskoutErrors(true);
options.SetSetLLDBGlobals(false);
if (ExecuteOneLineWithReturn(command_stream.GetData(),
ScriptInterpreter::eScriptReturnTypeBool,
&result, options))
return result;
return false;
}
ScriptInterpreterPythonImpl::SynchronicityHandler::SynchronicityHandler(
lldb::DebuggerSP debugger_sp, ScriptedCommandSynchronicity synchro)
: m_debugger_sp(debugger_sp), m_synch_wanted(synchro),
m_old_asynch(debugger_sp->GetAsyncExecution()) {
if (m_synch_wanted == eScriptedCommandSynchronicitySynchronous)
m_debugger_sp->SetAsyncExecution(false);
else if (m_synch_wanted == eScriptedCommandSynchronicityAsynchronous)
m_debugger_sp->SetAsyncExecution(true);
}
ScriptInterpreterPythonImpl::SynchronicityHandler::~SynchronicityHandler() {
if (m_synch_wanted != eScriptedCommandSynchronicityCurrentValue)
m_debugger_sp->SetAsyncExecution(m_old_asynch);
}
bool ScriptInterpreterPythonImpl::RunScriptBasedCommand(
const char *impl_function, llvm::StringRef args,
ScriptedCommandSynchronicity synchronicity,
lldb_private::CommandReturnObject &cmd_retobj, Status &error,
const lldb_private::ExecutionContext &exe_ctx) {
if (!impl_function) {
error = Status::FromErrorString("no function to execute");
return false;
}
lldb::DebuggerSP debugger_sp = m_debugger.shared_from_this();
lldb::ExecutionContextRefSP exe_ctx_ref_sp(new ExecutionContextRef(exe_ctx));
if (!debugger_sp.get()) {
error = Status::FromErrorString("invalid Debugger pointer");
return false;
}
bool ret_val = false;
{
Locker py_lock(this,
Locker::AcquireLock | Locker::InitSession |
(cmd_retobj.GetInteractive() ? 0 : Locker::NoSTDIN),
<rdar://problem/13010007> Added the ability for OS plug-ins to lazily populate the thread this. The python OS plug-in classes can now implement the following method: class OperatingSystemPlugin: def create_thread(self, tid, context): # Return a dictionary for a new thread to create it on demand This will add a new thread to the thread list if it doesn't already exist. The example code in lldb/examples/python/operating_system.py has been updated to show how this call us used. Cleaned up the code in PythonDataObjects.cpp/h: - renamed all classes that started with PythonData* to be Python*. - renamed PythonArray to PythonList. Cleaned up the code to use inheritance where - Centralized the code that does ref counting in the PythonObject class to a single function. - Made the "bool PythonObject::Reset(PyObject *)" function be virtual so each subclass can correctly check to ensure a PyObject is of the right type before adopting the object. - Cleaned up all APIs and added new constructors for the Python* classes to they can all construct form: - PyObject * - const PythonObject & - const lldb::ScriptInterpreterObjectSP & Cleaned up code in ScriptInterpreterPython: - Made calling python functions safer by templatizing the production of value formats. Python specifies the value formats based on built in C types (long, long long, etc), and code often uses typedefs for uint32_t, uint64_t, etc when passing arguments down to python. We will now always produce correct value formats as the templatized code will "do the right thing" all the time. - Fixed issues with the ScriptInterpreterPython::Locker where entering the session and leaving the session had a bunch of issues that could cause the "lldb" module globals lldb.debugger, lldb.target, lldb.process, lldb.thread, and lldb.frame to not be initialized. llvm-svn: 172873
2013-01-18 14:41:08 -09:00
Locker::FreeLock | Locker::TearDownSession);
SynchronicityHandler synch_handler(debugger_sp, synchronicity);
std::string args_str = args.str();
ret_val = SWIGBridge::LLDBSwigPythonCallCommand(
impl_function, m_dictionary_name.c_str(), debugger_sp, args_str.c_str(),
cmd_retobj, exe_ctx_ref_sp);
}
if (!ret_val) {
error = Status::FromErrorString("unable to execute script function");
return false;
}
if (cmd_retobj.GetStatus() == eReturnStatusFailed)
return false;
error.Clear();
return ret_val;
}
/// In Python, a special attribute __doc__ contains the docstring for an object
/// (function, method, class, ...) if any is defined Otherwise, the attribute's
/// value is None.
bool ScriptInterpreterPythonImpl::GetDocumentationForItem(const char *item,
std::string &dest) {
dest.clear();
if (!item || !*item)
return false;
std::string command(item);
command += ".__doc__";
// Python is going to point this to valid data if ExecuteOneLineWithReturn
// returns successfully.
char *result_ptr = nullptr;
if (ExecuteOneLineWithReturn(
command, ScriptInterpreter::eScriptReturnTypeCharStrOrNone,
&result_ptr, ExecuteScriptOptions().SetEnableIO(false))) {
if (result_ptr)
dest.assign(result_ptr);
return true;
}
StreamString str_stream;
str_stream << "Function " << item
<< " was not found. Containing module might be missing.";
dest = std::string(str_stream.GetString());
return false;
}
std::unique_ptr<ScriptInterpreterLocker>
ScriptInterpreterPythonImpl::AcquireInterpreterLock() {
std::unique_ptr<ScriptInterpreterLocker> py_lock(new Locker(
this, Locker::AcquireLock | Locker::InitSession | Locker::NoSTDIN,
Locker::FreeLock | Locker::TearDownSession));
return py_lock;
}
void ScriptInterpreterPythonImpl::Initialize() {
LLDB_SCOPED_TIMER();
// RAII-based initialization which correctly handles multiple-initialization,
// version- specific differences among Python 2 and Python 3, and saving and
// restoring various other pieces of state that can get mucked with during
// initialization.
InitializePythonRAII initialize_guard;
LLDBSwigPyInit();
// Update the path python uses to search for modules to include the current
// directory.
RunSimpleString("import sys");
AddToSysPath(AddLocation::End, ".");
// Don't denormalize paths when calling file_spec.GetPath(). On platforms
// that use a backslash as the path separator, this will result in executing
// python code containing paths with unescaped backslashes. But Python also
// accepts forward slashes, so to make life easier we just use that.
if (FileSpec file_spec = GetPythonDir())
AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false));
if (FileSpec file_spec = HostInfo::GetShlibDir())
AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false));
RunSimpleString("sys.dont_write_bytecode = 1; import "
"lldb.embedded_interpreter; from "
"lldb.embedded_interpreter import run_python_interpreter; "
"from lldb.embedded_interpreter import run_one_line");
[lldb] Fix that the embedded Python REPL crashes if it receives SIGINT When LLDB receives a SIGINT while running the embedded Python REPL it currently just crashes in ScriptInterpreterPythonImpl::Interrupt with an error such as the one below: Fatal Python error: PyThreadState_Get: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) The faulty code that causes this error is this part of ScriptInterpreterPythonImpl::Interrupt: PyThreadState *state = PyThreadState_GET(); if (!state) state = GetThreadState(); if (state) { long tid = state->thread_id; PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); The obvious fix I tried is to just acquire the GIL before this code is running which fixes the crash but the KeyboardInterrupt we want to raise immediately is actually just queued and would only be raised once the next line of input has been parsed (which e.g. won't interrupt Python code that is currently waiting on a timer or IO from what I can see). Also none of the functions we call here is marked as safe to be called from a signal handler from what I can see, so we might still end up crashing here with some bad timing. Python 3.2 introduced PyErr_SetInterrupt to solve this and the function takes care of all the details and avoids doing anything that isn't safe to do inside a signal handler. The only thing we need to do is to manually setup our own fake SIGINT handler that behaves the same way as the standalone Python REPL signal handler (which raises a KeyboardInterrupt). From what I understand the old code used to work with Python 2 so I kept the old code around until we officially drop support for Python 2. There is a small gap here with Python 3.0->3.1 where we might still be crashing, but those versions have reached their EOL more than a decade ago so I think we don't need to bother about them. Differential revision: https://reviews.llvm.org/D104886
2022-01-13 10:26:20 -09:00
#if LLDB_USE_PYTHON_SET_INTERRUPT
// Python will not just overwrite its internal SIGINT handler but also the
// one from the process. Backup the current SIGINT handler to prevent that
// Python deletes it.
RestoreSignalHandlerScope save_sigint(SIGINT);
// Setup a default SIGINT signal handler that works the same way as the
// normal Python REPL signal handler which raises a KeyboardInterrupt.
// Also make sure to not pollute the user's REPL with the signal module nor
// our utility function.
RunSimpleString("def lldb_setup_sigint_handler():\n"
" import signal;\n"
" def signal_handler(sig, frame):\n"
" raise KeyboardInterrupt()\n"
" signal.signal(signal.SIGINT, signal_handler);\n"
"lldb_setup_sigint_handler();\n"
"del lldb_setup_sigint_handler\n");
[lldb] Fix that the embedded Python REPL crashes if it receives SIGINT When LLDB receives a SIGINT while running the embedded Python REPL it currently just crashes in ScriptInterpreterPythonImpl::Interrupt with an error such as the one below: Fatal Python error: PyThreadState_Get: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) The faulty code that causes this error is this part of ScriptInterpreterPythonImpl::Interrupt: PyThreadState *state = PyThreadState_GET(); if (!state) state = GetThreadState(); if (state) { long tid = state->thread_id; PyThreadState_Swap(state); int num_threads = PyThreadState_SetAsyncExc(tid, PyExc_KeyboardInterrupt); The obvious fix I tried is to just acquire the GIL before this code is running which fixes the crash but the KeyboardInterrupt we want to raise immediately is actually just queued and would only be raised once the next line of input has been parsed (which e.g. won't interrupt Python code that is currently waiting on a timer or IO from what I can see). Also none of the functions we call here is marked as safe to be called from a signal handler from what I can see, so we might still end up crashing here with some bad timing. Python 3.2 introduced PyErr_SetInterrupt to solve this and the function takes care of all the details and avoids doing anything that isn't safe to do inside a signal handler. The only thing we need to do is to manually setup our own fake SIGINT handler that behaves the same way as the standalone Python REPL signal handler (which raises a KeyboardInterrupt). From what I understand the old code used to work with Python 2 so I kept the old code around until we officially drop support for Python 2. There is a small gap here with Python 3.0->3.1 where we might still be crashing, but those versions have reached their EOL more than a decade ago so I think we don't need to bother about them. Differential revision: https://reviews.llvm.org/D104886
2022-01-13 10:26:20 -09:00
#endif
}
void ScriptInterpreterPythonImpl::AddToSysPath(AddLocation location,
std::string path) {
std::string statement;
if (location == AddLocation::Beginning) {
statement.assign("sys.path.insert(0,\"");
statement.append(path);
statement.append("\")");
} else {
statement.assign("sys.path.append(\"");
statement.append(path);
statement.append("\")");
}
RunSimpleString(statement.c_str());
}
// We are intentionally NOT calling Py_Finalize here (this would be the logical
// place to call it). Calling Py_Finalize here causes test suite runs to seg
// fault: The test suite runs in Python. It registers SBDebugger::Terminate to
// be called 'at_exit'. When the test suite Python harness finishes up, it
// calls Py_Finalize, which calls all the 'at_exit' registered functions.
// SBDebugger::Terminate calls Debugger::Terminate, which calls lldb::Terminate,
// which calls ScriptInterpreter::Terminate, which calls
// ScriptInterpreterPythonImpl::Terminate. So if we call Py_Finalize here, we
// end up with Py_Finalize being called from within Py_Finalize, which results
// in a seg fault. Since this function only gets called when lldb is shutting
// down and going away anyway, the fact that we don't actually call Py_Finalize
// should not cause any problems (everything should shut down/go away anyway
// when the process exits).
//
// void ScriptInterpreterPythonImpl::Terminate() { Py_Finalize (); }