1 //===-- PythonExceptionState.cpp --------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #ifndef LLDB_DISABLE_PYTHON
11 
12 #include "lldb-python.h"
13 #include "PythonExceptionState.h"
14 
15 #include "llvm/ADT/StringRef.h"
16 #include "llvm/Support/raw_ostream.h"
17 
18 using namespace lldb_private;
19 
20 PythonExceptionState::PythonExceptionState(bool restore_on_exit)
21     : m_restore_on_exit(restore_on_exit)
22 {
23     Acquire(restore_on_exit);
24 }
25 
26 PythonExceptionState::~PythonExceptionState()
27 {
28     if (m_restore_on_exit)
29         Restore();
30 }
31 
32 void
33 PythonExceptionState::Acquire(bool restore_on_exit)
34 {
35     // If a state is already acquired, the user needs to decide whether they
36     // want to discard or restore it.  Don't allow the potential silent
37     // loss of a valid state.
38     assert(!IsError());
39 
40     if (!HasErrorOccurred())
41         return;
42 
43     PyObject *py_type = nullptr;
44     PyObject *py_value = nullptr;
45     PyObject *py_traceback = nullptr;
46     PyErr_Fetch(&py_type, &py_value, &py_traceback);
47     // PyErr_Fetch clears the error flag.
48     assert(!HasErrorOccurred());
49 
50     // Ownership of the objects returned by `PyErr_Fetch` is transferred
51     // to us.
52     m_type.Reset(PyRefType::Owned, py_type);
53     m_value.Reset(PyRefType::Owned, py_value);
54     m_traceback.Reset(PyRefType::Owned, py_traceback);
55     m_restore_on_exit = restore_on_exit;
56 }
57 
58 void
59 PythonExceptionState::Restore()
60 {
61     if (m_type.IsValid())
62     {
63         // The documentation for PyErr_Restore says "Do not pass a null type and
64         // non-null value or traceback.  So only restore if type was non-null
65         // to begin with.  In this case we're passing ownership back to Python
66         // so release them all.
67         PyErr_Restore(m_type.release(), m_value.release(), m_traceback.release());
68     }
69 
70     // After we restore, we should not hold onto the exception state.  Demand that
71     // it be re-acquired.
72     Discard();
73 }
74 
75 void
76 PythonExceptionState::Discard()
77 {
78     m_type.Reset();
79     m_value.Reset();
80     m_traceback.Reset();
81 }
82 
83 void
84 PythonExceptionState::Reset()
85 {
86     if (m_restore_on_exit)
87         Restore();
88     else
89         Discard();
90 }
91 
92 bool
93 PythonExceptionState::HasErrorOccurred()
94 {
95     return PyErr_Occurred();
96 }
97 
98 bool
99 PythonExceptionState::IsError() const
100 {
101     return m_type.IsValid() || m_value.IsValid() || m_traceback.IsValid();
102 }
103 
104 PythonObject
105 PythonExceptionState::GetType() const
106 {
107     return m_type;
108 }
109 
110 PythonObject
111 PythonExceptionState::GetValue() const
112 {
113     return m_value;
114 }
115 
116 PythonObject
117 PythonExceptionState::GetTraceback() const
118 {
119     return m_traceback;
120 }
121 
122 std::string
123 PythonExceptionState::Format() const
124 {
125     // Don't allow this function to modify the error state.
126     PythonExceptionState state(true);
127 
128     std::string backtrace = ReadBacktrace();
129     if (!IsError())
130         return std::string();
131 
132     // It's possible that ReadPythonBacktrace generated another exception.
133     // If this happens we have to clear the exception, because otherwise
134     // PyObject_Str() will assert below.  That's why we needed to do the
135     // save / restore at the beginning of this function.
136     PythonExceptionState bt_error_state(false);
137 
138     std::string error_string;
139     llvm::raw_string_ostream error_stream(error_string);
140     error_stream << m_value.Str().GetString() << "\n";
141 
142     if (!bt_error_state.IsError())
143     {
144         // If we were able to read the backtrace, just append it.
145         error_stream << backtrace << "\n";
146     }
147     else
148     {
149         // Otherwise, append some information about why we were unable to
150         // obtain the backtrace.
151         PythonString bt_error = bt_error_state.GetValue().Str();
152         error_stream << "An error occurred while retrieving the backtrace: " << bt_error.GetString() << "\n";
153     }
154     return error_stream.str();
155 }
156 
157 std::string
158 PythonExceptionState::ReadBacktrace() const
159 {
160     std::string retval("backtrace unavailable");
161 
162     auto traceback_module = PythonModule::ImportModule("traceback");
163 #if PY_MAJOR_VERSION >= 3
164     auto stringIO_module = PythonModule::ImportModule("io");
165 #else
166     auto stringIO_module = PythonModule::ImportModule("StringIO");
167 #endif
168     if (!m_traceback.IsAllocated())
169         return retval;
170 
171     if (!traceback_module.IsAllocated() || !stringIO_module.IsAllocated())
172         return retval;
173 
174     auto stringIO_builder = stringIO_module.ResolveName<PythonCallable>("StringIO");
175     if (!stringIO_builder.IsAllocated())
176         return retval;
177 
178     auto stringIO_buffer = stringIO_builder();
179     if (!stringIO_buffer.IsAllocated())
180         return retval;
181 
182     auto printTB = traceback_module.ResolveName<PythonCallable>("print_tb");
183     if (!printTB.IsAllocated())
184         return retval;
185 
186     auto printTB_result = printTB(m_traceback.get(), Py_None, stringIO_buffer.get());
187     auto stringIO_getvalue = stringIO_buffer.ResolveName<PythonCallable>("getvalue");
188     if (!stringIO_getvalue.IsAllocated())
189         return retval;
190 
191     auto printTB_string = stringIO_getvalue().AsType<PythonString>();
192     if (!printTB_string.IsAllocated())
193         return retval;
194 
195     llvm::StringRef string_data(printTB_string.GetString());
196     retval.assign(string_data.data(), string_data.size());
197 
198     return retval;
199 }
200 
201 #endif
202