| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "python_private.h" |
| #include "qpid/dispatch/python_embedded.h" // must be first! |
| |
| #include "qpid/dispatch/error.h" |
| |
| #include "aprintf.h" |
| #include "log_private.h" |
| |
| #include "qpid/dispatch/enum.h" |
| #include "qpid/dispatch/log.h" |
| |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <string.h> |
| |
| // force the use of the non-gnu version of strerror_r |
| #undef _POSIX_C_SOURCE |
| #undef _GNU_SOURCE |
| #define _POSIX_C_SOURCE 200112L |
| #define _GNU_SOURCE 0 |
| #include <stdio.h> |
| |
| |
| static const char *qd_error_names[] = { |
| "No Error", |
| "Not found", |
| "Already exists", |
| "Allocation", |
| "Invalid message", |
| "Python", |
| "Configuration", |
| "Type", |
| "Value", |
| "Run Time", |
| "System" |
| }; |
| ENUM_DEFINE(qd_error, qd_error_names); |
| |
| #define ERROR_MAX QD_LOG_TEXT_MAX |
| const int QD_ERROR_MAX = ERROR_MAX; |
| |
| /* Thread local data. */ |
| static __thread struct { |
| char error_message[ERROR_MAX]; |
| qd_error_t error_code; |
| } ts = {{0}, 0}; |
| |
| static qd_log_source_t* log_source = 0; |
| |
| void qd_error_initialize() { |
| log_source = qd_log_source("ERROR"); |
| } |
| |
| qd_error_t qd_error_vimpl(qd_error_t code, const char *file, int line, const char *fmt, va_list ap) { |
| ts.error_code = code; |
| if (code) { |
| char *begin = ts.error_message; |
| char *end = begin + ERROR_MAX; |
| (void)aprintf; |
| const char* name = qd_error_name(code); |
| if (name) |
| aprintf(&begin, end, "%s: ", name); |
| else |
| aprintf(&begin, end, "%d: ", code); |
| vaprintf(&begin, end, fmt, ap); |
| // NOTE: Use the file/line from the qd_error macro, not this line in error.c |
| qd_log_impl(log_source, QD_LOG_ERROR, file, line, "%s", qd_error_message()); |
| return code; |
| } |
| else |
| qd_error_clear(); |
| return 0; |
| } |
| |
| qd_error_t qd_error_impl(qd_error_t code, const char *file, int line, const char *fmt, ...) { |
| va_list ap; |
| va_start(ap, fmt); |
| qd_error_t err = qd_error_vimpl(code, file, line, fmt, ap); |
| va_end(ap); |
| return err; |
| } |
| |
| qd_error_t qd_error_clear() { |
| ts.error_code = 0; |
| snprintf(ts.error_message, ERROR_MAX, "No Error"); |
| return QD_ERROR_NONE; |
| } |
| |
| const char* qd_error_message() { |
| return ts.error_message; |
| } |
| |
| qd_error_t qd_error_code() { |
| return ts.error_code; |
| } |
| |
| static void py_set_item(PyObject *dict, const char* name, PyObject *value) { |
| PyObject *py_name = PyUnicode_FromString(name); |
| PyDict_SetItem(dict, py_name, value); |
| Py_DECREF(py_name); |
| } |
| |
| static void log_trace_py(PyObject *type, PyObject *value, PyObject* trace, qd_log_level_t level, |
| const char *file, int line) |
| { |
| if (!qd_log_enabled(log_source, level)) return; |
| if (!(type && value && trace)) return; |
| |
| PyObject *module = PyImport_ImportModule("traceback"); |
| if (!module) return; |
| |
| PyObject *globals = PyDict_New(); |
| py_set_item(globals, "traceback", module); |
| Py_DECREF(module); |
| |
| PyObject *locals = PyDict_New(); |
| py_set_item(locals, "type", type); |
| py_set_item(locals, "value", value); |
| py_set_item(locals, "trace", trace); |
| |
| PyObject *result = PyRun_String( |
| "''.join(traceback.format_exception(type, value, trace))", Py_eval_input, globals, locals); |
| Py_DECREF(globals); |
| Py_DECREF(locals); |
| |
| |
| if (result) { |
| char *trace = py_string_2_c(result); |
| if (trace) { |
| if (strlen(trace) < QD_LOG_TEXT_MAX) { |
| qd_log_impl(log_source, level, file, line, "%s", trace); |
| } else { |
| // Keep as much of the the tail of the trace as we can. |
| const char *tail = trace; |
| while (tail && strlen(tail) > QD_LOG_TEXT_MAX) { |
| tail = strchr(tail, '\n'); |
| if (tail) ++tail; |
| } |
| qd_log_impl(log_source, level, file, line, |
| "Traceback truncated:\n%s", tail ? tail : ""); |
| } |
| free(trace); |
| } |
| Py_DECREF(result); |
| } |
| } |
| |
| qd_error_t qd_error_py_impl(const char *file, int line) { |
| qd_python_check_lock(); |
| if (PyErr_Occurred()) { |
| PyObject *type, *value, *trace; |
| PyErr_Fetch(&type, &value, &trace); /* Note clears the python error indicator */ |
| |
| PyObject *py_type_name = type ? PyObject_GetAttrString(type, "__name__") : NULL; |
| char *type_name = py_type_name ? py_string_2_c(py_type_name) : NULL; |
| PyObject *py_value_str = value ? PyObject_Str(value) : NULL; |
| char *value_str = py_string_2_c(py_value_str); |
| |
| PyErr_Clear(); /* Ignore errors while we're trying to build the values. */ |
| if (type_name) |
| qd_error_impl(QD_ERROR_PYTHON, file, line, "%s: %s", type_name, |
| (value_str) ? value_str : "Unknown"); |
| else |
| qd_error_impl(QD_ERROR_PYTHON, file, line, "%s", |
| (value_str) ? value_str : "Unknown"); |
| Py_XDECREF(py_value_str); |
| free(value_str); |
| Py_XDECREF(py_type_name); |
| free(type_name); |
| |
| log_trace_py(type, value, trace, QD_LOG_ERROR, file, line); |
| |
| Py_XDECREF(type); |
| Py_XDECREF(value); |
| Py_XDECREF(trace); |
| } else { |
| qd_error_clear(); |
| } |
| return qd_error_code(); |
| } |
| |
| qd_error_t qd_error_errno_impl(int errnum, const char *file, int line, const char *fmt, ...) { |
| if (errnum) { |
| ts.error_code = QD_ERROR_SYSTEM; |
| char *begin = ts.error_message; |
| char *end = begin + sizeof(ts.error_message); |
| va_list arglist; |
| va_start(arglist, fmt); |
| vaprintf(&begin, end, fmt, arglist); |
| va_end(arglist); |
| aprintf(&begin, end, ": ", errnum); |
| char *em = ts.error_message; |
| if(strerror_r(errnum, begin, end - begin) != 0) { |
| snprintf(begin, end - begin, "Unknown error %d", errnum); |
| } |
| qd_log_impl(log_source, QD_LOG_ERROR, file, line, "%s", em); |
| return qd_error_code(); |
| } |
| else |
| return qd_error_clear(); |
| } |