summaryrefslogtreecommitdiffstats
path: root/contrib/llvm/tools/lldb/source/Plugins/ScriptInterpreter/Python/PythonExceptionState.cpp
blob: 2cbd85bfa11ef8546ce3805ff1c0ba270317fecc (plain)
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
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
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<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
OpenPOWER on IntegriCloud