1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
//===-- 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
|