From 66b75430a93929d6fc6ed63db14ca28e3ad5b1f6 Mon Sep 17 00:00:00 2001 From: dim Date: Wed, 30 Dec 2015 11:55:28 +0000 Subject: Vendor import of stripped lldb trunk r256633: https://llvm.org/svn/llvm-project/lldb/trunk@256633 --- .../Python/PythonExceptionState.cpp | 201 +++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp (limited to 'source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp') diff --git a/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp b/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp new file mode 100644 index 0000000..2cbd85b --- /dev/null +++ b/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp @@ -0,0 +1,201 @@ +//===-- 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 + +#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("StringIO"); + if (!stringIO_builder.IsAllocated()) + return retval; + + auto stringIO_buffer = stringIO_builder(); + if (!stringIO_buffer.IsAllocated()) + return retval; + + auto printTB = traceback_module.ResolveName("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("getvalue"); + if (!stringIO_getvalue.IsAllocated()) + return retval; + + auto printTB_string = stringIO_getvalue().AsType(); + if (!printTB_string.IsAllocated()) + return retval; + + llvm::StringRef string_data(printTB_string.GetString()); + retval.assign(string_data.data(), string_data.size()); + + return retval; +} + +#endif -- cgit v1.1