mirror of
https://github.com/Gericom/teak-llvm.git
synced 2025-06-28 15:58:57 -04:00

to reflect the new license. We understand that people may be surprised that we're moving the header entirely to discuss the new license. We checked this carefully with the Foundation's lawyer and we believe this is the correct approach. Essentially, all code in the project is now made available by the LLVM project under our new license, so you will see that the license headers include that license only. Some of our contributors have contributed code under our old license, and accordingly, we have retained a copy of our old license notice in the top-level files in each project and repository. llvm-svn: 351636
170 lines
5.2 KiB
C++
170 lines
5.2 KiB
C++
//===-- PythonExceptionState.cpp --------------------------------*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#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
|