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