| //===-- PythonExceptionState.cpp --------------------------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLDB_DISABLE_PYTHON |
| |
| // LLDB Python header must be included first |
| #include "lldb-python.h" |
| |
| #include "PythonExceptionState.h" |
| |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/raw_ostream.h" |
| |
| using namespace lldb_private; |
| |
| PythonExceptionState::PythonExceptionState(bool restore_on_exit) |
| : m_restore_on_exit(restore_on_exit) { |
| Acquire(restore_on_exit); |
| } |
| |
| PythonExceptionState::~PythonExceptionState() { |
| if (m_restore_on_exit) |
| Restore(); |
| } |
| |
| void PythonExceptionState::Acquire(bool restore_on_exit) { |
| // If a state is already acquired, the user needs to decide whether they want |
| // to discard or restore it. Don't allow the potential silent loss of a |
| // valid state. |
| assert(!IsError()); |
| |
| if (!HasErrorOccurred()) |
| return; |
| |
| PyObject *py_type = nullptr; |
| PyObject *py_value = nullptr; |
| PyObject *py_traceback = nullptr; |
| PyErr_Fetch(&py_type, &py_value, &py_traceback); |
| // PyErr_Fetch clears the error flag. |
| assert(!HasErrorOccurred()); |
| |
| // Ownership of the objects returned by `PyErr_Fetch` is transferred to us. |
| m_type.Reset(PyRefType::Owned, py_type); |
| m_value.Reset(PyRefType::Owned, py_value); |
| m_traceback.Reset(PyRefType::Owned, py_traceback); |
| m_restore_on_exit = restore_on_exit; |
| } |
| |
| void PythonExceptionState::Restore() { |
| if (m_type.IsValid()) { |
| // The documentation for PyErr_Restore says "Do not pass a null type and |
| // non-null value or traceback. So only restore if type was non-null to |
| // begin with. In this case we're passing ownership back to Python so |
| // release them all. |
| PyErr_Restore(m_type.release(), m_value.release(), m_traceback.release()); |
| } |
| |
| // After we restore, we should not hold onto the exception state. Demand |
| // that it be re-acquired. |
| Discard(); |
| } |
| |
| void PythonExceptionState::Discard() { |
| m_type.Reset(); |
| m_value.Reset(); |
| m_traceback.Reset(); |
| } |
| |
| void PythonExceptionState::Reset() { |
| if (m_restore_on_exit) |
| Restore(); |
| else |
| Discard(); |
| } |
| |
| bool PythonExceptionState::HasErrorOccurred() { return PyErr_Occurred(); } |
| |
| bool PythonExceptionState::IsError() const { |
| return m_type.IsValid() || m_value.IsValid() || m_traceback.IsValid(); |
| } |
| |
| PythonObject PythonExceptionState::GetType() const { return m_type; } |
| |
| PythonObject PythonExceptionState::GetValue() const { return m_value; } |
| |
| PythonObject PythonExceptionState::GetTraceback() const { return m_traceback; } |
| |
| std::string PythonExceptionState::Format() const { |
| // Don't allow this function to modify the error state. |
| PythonExceptionState state(true); |
| |
| std::string backtrace = ReadBacktrace(); |
| if (!IsError()) |
| return std::string(); |
| |
| // It's possible that ReadPythonBacktrace generated another exception. If |
| // this happens we have to clear the exception, because otherwise |
| // PyObject_Str() will assert below. That's why we needed to do the save / |
| // restore at the beginning of this function. |
| PythonExceptionState bt_error_state(false); |
| |
| std::string error_string; |
| llvm::raw_string_ostream error_stream(error_string); |
| error_stream << m_value.Str().GetString() << "\n"; |
| |
| if (!bt_error_state.IsError()) { |
| // If we were able to read the backtrace, just append it. |
| error_stream << backtrace << "\n"; |
| } else { |
| // Otherwise, append some information about why we were unable to obtain |
| // the backtrace. |
| PythonString bt_error = bt_error_state.GetValue().Str(); |
| error_stream << "An error occurred while retrieving the backtrace: " |
| << bt_error.GetString() << "\n"; |
| } |
| return error_stream.str(); |
| } |
| |
| std::string PythonExceptionState::ReadBacktrace() const { |
| std::string retval("backtrace unavailable"); |
| |
| auto traceback_module = PythonModule::ImportModule("traceback"); |
| #if PY_MAJOR_VERSION >= 3 |
| auto stringIO_module = PythonModule::ImportModule("io"); |
| #else |
| auto stringIO_module = PythonModule::ImportModule("StringIO"); |
| #endif |
| if (!m_traceback.IsAllocated()) |
| return retval; |
| |
| if (!traceback_module.IsAllocated() || !stringIO_module.IsAllocated()) |
| return retval; |
| |
| auto stringIO_builder = |
| stringIO_module.ResolveName<PythonCallable>("StringIO"); |
| if (!stringIO_builder.IsAllocated()) |
| return retval; |
| |
| auto stringIO_buffer = stringIO_builder(); |
| if (!stringIO_buffer.IsAllocated()) |
| return retval; |
| |
| auto printTB = traceback_module.ResolveName<PythonCallable>("print_tb"); |
| if (!printTB.IsAllocated()) |
| return retval; |
| |
| auto printTB_result = |
| printTB(m_traceback.get(), Py_None, stringIO_buffer.get()); |
| auto stringIO_getvalue = |
| stringIO_buffer.ResolveName<PythonCallable>("getvalue"); |
| if (!stringIO_getvalue.IsAllocated()) |
| return retval; |
| |
| auto printTB_string = stringIO_getvalue().AsType<PythonString>(); |
| if (!printTB_string.IsAllocated()) |
| return retval; |
| |
| llvm::StringRef string_data(printTB_string.GetString()); |
| retval.assign(string_data.data(), string_data.size()); |
| |
| return retval; |
| } |
| |
| #endif |