1 //===-- PythonExceptionState.cpp --------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLDB_DISABLE_PYTHON
11 // LLDB Python header must be included first
12 #include "lldb-python.h"
14 #include "PythonExceptionState.h"
16 #include "llvm/ADT/StringRef.h"
17 #include "llvm/Support/raw_ostream.h"
19 using namespace lldb_private;
21 PythonExceptionState::PythonExceptionState(bool restore_on_exit)
22 : m_restore_on_exit(restore_on_exit) {
23 Acquire(restore_on_exit);
26 PythonExceptionState::~PythonExceptionState() {
27 if (m_restore_on_exit)
31 void PythonExceptionState::Acquire(bool restore_on_exit) {
32 // If a state is already acquired, the user needs to decide whether they want
33 // to discard or restore it. Don't allow the potential silent loss of a
37 if (!HasErrorOccurred())
40 PyObject *py_type = nullptr;
41 PyObject *py_value = nullptr;
42 PyObject *py_traceback = nullptr;
43 PyErr_Fetch(&py_type, &py_value, &py_traceback);
44 // PyErr_Fetch clears the error flag.
45 assert(!HasErrorOccurred());
47 // Ownership of the objects returned by `PyErr_Fetch` is transferred to us.
48 m_type.Reset(PyRefType::Owned, py_type);
49 m_value.Reset(PyRefType::Owned, py_value);
50 m_traceback.Reset(PyRefType::Owned, py_traceback);
51 m_restore_on_exit = restore_on_exit;
54 void PythonExceptionState::Restore() {
55 if (m_type.IsValid()) {
56 // The documentation for PyErr_Restore says "Do not pass a null type and
57 // non-null value or traceback. So only restore if type was non-null to
58 // begin with. In this case we're passing ownership back to Python so
60 PyErr_Restore(m_type.release(), m_value.release(), m_traceback.release());
63 // After we restore, we should not hold onto the exception state. Demand
64 // that it be re-acquired.
68 void PythonExceptionState::Discard() {
74 void PythonExceptionState::Reset() {
75 if (m_restore_on_exit)
81 bool PythonExceptionState::HasErrorOccurred() { return PyErr_Occurred(); }
83 bool PythonExceptionState::IsError() const {
84 return m_type.IsValid() || m_value.IsValid() || m_traceback.IsValid();
87 PythonObject PythonExceptionState::GetType() const { return m_type; }
89 PythonObject PythonExceptionState::GetValue() const { return m_value; }
91 PythonObject PythonExceptionState::GetTraceback() const { return m_traceback; }
93 std::string PythonExceptionState::Format() const {
94 // Don't allow this function to modify the error state.
95 PythonExceptionState state(true);
97 std::string backtrace = ReadBacktrace();
101 // It's possible that ReadPythonBacktrace generated another exception. If
102 // this happens we have to clear the exception, because otherwise
103 // PyObject_Str() will assert below. That's why we needed to do the save /
104 // restore at the beginning of this function.
105 PythonExceptionState bt_error_state(false);
107 std::string error_string;
108 llvm::raw_string_ostream error_stream(error_string);
109 error_stream << m_value.Str().GetString() << "\n";
111 if (!bt_error_state.IsError()) {
112 // If we were able to read the backtrace, just append it.
113 error_stream << backtrace << "\n";
115 // Otherwise, append some information about why we were unable to obtain
117 PythonString bt_error = bt_error_state.GetValue().Str();
118 error_stream << "An error occurred while retrieving the backtrace: "
119 << bt_error.GetString() << "\n";
121 return error_stream.str();
124 std::string PythonExceptionState::ReadBacktrace() const {
125 std::string retval("backtrace unavailable");
127 auto traceback_module = PythonModule::ImportModule("traceback");
128 #if PY_MAJOR_VERSION >= 3
129 auto stringIO_module = PythonModule::ImportModule("io");
131 auto stringIO_module = PythonModule::ImportModule("StringIO");
133 if (!m_traceback.IsAllocated())
136 if (!traceback_module.IsAllocated() || !stringIO_module.IsAllocated())
139 auto stringIO_builder =
140 stringIO_module.ResolveName<PythonCallable>("StringIO");
141 if (!stringIO_builder.IsAllocated())
144 auto stringIO_buffer = stringIO_builder();
145 if (!stringIO_buffer.IsAllocated())
148 auto printTB = traceback_module.ResolveName<PythonCallable>("print_tb");
149 if (!printTB.IsAllocated())
152 auto printTB_result =
153 printTB(m_traceback.get(), Py_None, stringIO_buffer.get());
154 auto stringIO_getvalue =
155 stringIO_buffer.ResolveName<PythonCallable>("getvalue");
156 if (!stringIO_getvalue.IsAllocated())
159 auto printTB_string = stringIO_getvalue().AsType<PythonString>();
160 if (!printTB_string.IsAllocated())
163 llvm::StringRef string_data(printTB_string.GetString());
164 retval.assign(string_data.data(), string_data.size());