diff options
Diffstat (limited to 'src/libs/xpcom18a4/python/src/ErrorUtils.cpp')
-rw-r--r-- | src/libs/xpcom18a4/python/src/ErrorUtils.cpp | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/src/libs/xpcom18a4/python/src/ErrorUtils.cpp b/src/libs/xpcom18a4/python/src/ErrorUtils.cpp new file mode 100644 index 00000000..9400799b --- /dev/null +++ b/src/libs/xpcom18a4/python/src/ErrorUtils.cpp @@ -0,0 +1,483 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Python XPCOM language bindings. + * + * The Initial Developer of the Original Code is + * ActiveState Tool Corp. + * Portions created by the Initial Developer are Copyright (C) 2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Mark Hammond <MarkH@ActiveState.com> (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// +// This code is part of the XPCOM extensions for Python. +// +// Written May 2000 by Mark Hammond. +// +// Based heavily on the Python COM support, which is +// (c) Mark Hammond and Greg Stein. +// +// (c) 2000, ActiveState corp. + +#include "PyXPCOM_std.h" +#include "nsReadableUtils.h" +#include <nsIConsoleService.h> +#ifdef VBOX +# include <nsIExceptionService.h> +# include <iprt/err.h> +# include <iprt/string.h> +#endif +#include "nspr.h" // PR_fprintf + +static char *PyTraceback_AsString(PyObject *exc_tb); + +// The internal helper that actually moves the +// formatted string to the target! + +// Only used in really bad situations! +static void _PanicErrorWrite(const char *msg) +{ + nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID); + if (consoleService) + consoleService->LogStringMessage(NS_ConvertASCIItoUCS2(msg).get()); + PR_fprintf(PR_STDERR,"%s\n", msg); +} + +// Called when our "normal" error logger fails. +static void HandleLogError(const char *pszMessageText) +{ + nsCAutoString streamout; + + _PanicErrorWrite("Failed to log an error record"); + if (PyXPCOM_FormatCurrentException(streamout)) + _PanicErrorWrite(streamout.get()); + _PanicErrorWrite("Original error follows:"); + _PanicErrorWrite(pszMessageText); +} + +static const char *LOGGER_WARNING = "warning"; +static const char *LOGGER_ERROR = "error"; +static const char *LOGGER_DEBUG = "debug"; + +// Our "normal" error logger - calls back to the logging module. +void DoLogMessage(const char *methodName, const char *pszMessageText) +{ + // We use the logging module now. Originally this code called + // the logging module directly by way of the C API's + // PyImport_ImportModule/PyObject_CallMethod etc. However, this + // causes problems when there is no Python caller on the stack - + // the logging module's findCaller method fails with a None frame. + // We now work around this by calling PyRun_SimpleString - this + // causes a new frame to be created for executing the compiled + // string, and the logging module no longer fails. + // XXX - this implementation is less than ideal - findCaller now + // returns ("<string>", 2). Ideally we would compile with a + // filename something similar to "<pydom error reporter>". + + // But this also means we need a clear error state... + PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL; + PyErr_Fetch(&exc_typ, &exc_val, &exc_tb); +// We will execute: +// import logging +// logging.getLogger('xpcom').{warning/error/etc}("%s", {msg_text}) + nsCAutoString c("import logging\nlogging.getLogger('xpcom')."); + c += methodName; + c += "('%s', "; + // Pull a trick to ensure a valid string - use Python repr! +#if PY_MAJOR_VERSION <= 2 + PyObject *obMessage = PyString_FromString(pszMessageText); +#else + PyObject *obMessage = PyUnicode_FromString(pszMessageText); +#endif + if (obMessage) { + PyObject *repr = PyObject_Repr(obMessage); + if (repr) { +#if PY_MAJOR_VERSION <= 2 + c += PyString_AsString(repr); +#else + c += PyUnicode_AsUTF8(repr); +#endif + Py_DECREF(repr); + } + Py_DECREF(obMessage); + } + c += ")\n"; + if (PyRun_SimpleString(c.get()) != 0) { + HandleLogError(pszMessageText); + } + PyErr_Restore(exc_typ, exc_val, exc_tb); +} + +void LogMessage(const char *methodName, const char *pszMessageText) +{ + // Be careful to save and restore the Python exception state + // before calling back to Python, or we lose the original error. + PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL; + PyErr_Fetch( &exc_typ, &exc_val, &exc_tb); + DoLogMessage(methodName, pszMessageText); + PyErr_Restore(exc_typ, exc_val, exc_tb); +} + + +void LogMessage(const char *methodName, nsACString &text) +{ + char *c = ToNewCString(text); + LogMessage(methodName, c); + nsCRT::free(c); +} + +// A helper for the various logging routines. +static void VLogF(const char *methodName, const char *fmt, va_list argptr) +{ + char buff[512]; +#ifdef VBOX /* Enable the use of VBox formatting types. */ + RTStrPrintfV(buff, sizeof(buff), fmt, argptr); +#else + // Use safer NS_ functions. + PR_vsnprintf(buff, sizeof(buff), fmt, argptr); +#endif + + LogMessage(methodName, buff); +} + +PRBool PyXPCOM_FormatCurrentException(nsCString &streamout) +{ + PRBool ok = PR_FALSE; + PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL; + PyErr_Fetch( &exc_typ, &exc_val, &exc_tb); + PyErr_NormalizeException( &exc_typ, &exc_val, &exc_tb); + if (exc_typ) { + ok = PyXPCOM_FormatGivenException(streamout, exc_typ, exc_val, + exc_tb); + } + PyErr_Restore(exc_typ, exc_val, exc_tb); + return ok; +} + +PRBool PyXPCOM_FormatGivenException(nsCString &streamout, + PyObject *exc_typ, PyObject *exc_val, + PyObject *exc_tb) +{ + if (!exc_typ) + return PR_FALSE; + streamout += "\n"; + + if (exc_tb) { + const char *szTraceback = PyTraceback_AsString(exc_tb); + if (szTraceback == NULL) + streamout += "Can't get the traceback info!"; + else { + streamout += "Traceback (most recent call last):\n"; + streamout += szTraceback; + PyMem_Free((void *)szTraceback); + } + } + PyObject *temp = PyObject_Str(exc_typ); + if (temp) { +#if PY_MAJOR_VERSION <= 2 + streamout += PyString_AsString(temp); +#else + streamout += PyUnicode_AsUTF8(temp); +#endif + Py_DECREF(temp); + } else + streamout += "Can't convert exception to a string!"; + streamout += ": "; + if (exc_val != NULL) { + temp = PyObject_Str(exc_val); + if (temp) { +#if PY_MAJOR_VERSION <= 2 + streamout += PyString_AsString(temp); +#else + streamout += PyUnicode_AsUTF8(temp); +#endif + Py_DECREF(temp); + } else + streamout += "Can't convert exception value to a string!"; + } + return PR_TRUE; +} + +void PyXPCOM_LogError(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + // NOTE: It is tricky to use logger.exception here - the exception + // state when called back from the C code is clear. Only Python 2.4 + // and later allows an explicit exc_info tuple(). + + // Don't use VLogF here, instead arrange for exception info and + // traceback to be in the same buffer. + char buff[512]; + PR_vsnprintf(buff, sizeof(buff), fmt, marker); + // If we have a Python exception, also log that: + nsCAutoString streamout(buff); + if (PyXPCOM_FormatCurrentException(streamout)) { + LogMessage(LOGGER_ERROR, streamout); + } + va_end(marker); +} + +void PyXPCOM_LogWarning(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + VLogF(LOGGER_WARNING, fmt, marker); + va_end(marker); +} + +void PyXPCOM_Log(const char *level, const nsCString &msg) +{ + DoLogMessage(level, msg.get()); +} + +#ifdef DEBUG +void PyXPCOM_LogDebug(const char *fmt, ...) +{ + va_list marker; + va_start(marker, fmt); + VLogF(LOGGER_DEBUG, fmt, marker); + va_end(marker); +} +#endif + +#ifdef VBOX +PyObject *PyXPCOM_BuildErrorMessage(nsresult r) +{ + char msg[512]; + bool gotMsg = false; + + if (!gotMsg) + { + nsresult rc; + nsCOMPtr <nsIExceptionService> es; + es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc); + if (NS_SUCCEEDED (rc)) + { + nsCOMPtr <nsIExceptionManager> em; + rc = es->GetCurrentExceptionManager (getter_AddRefs (em)); + if (NS_SUCCEEDED (rc)) + { + nsCOMPtr <nsIException> ex; + rc = em->GetExceptionFromProvider(r, NULL, getter_AddRefs (ex)); + if (NS_SUCCEEDED (rc) && ex) + { + nsXPIDLCString emsg; + ex->GetMessage(getter_Copies(emsg)); + PR_snprintf(msg, sizeof(msg), "%s", + emsg.get()); + gotMsg = true; + } + } + } + } + + if (!gotMsg) + { + const RTCOMERRMSG* pMsg = RTErrCOMGet(r); + if (strncmp(pMsg->pszMsgFull, "Unknown", 7) != 0) + { + PR_snprintf(msg, sizeof(msg), "%s (%s)", + pMsg->pszMsgFull, pMsg->pszDefine); + gotMsg = true; + } + } + + if (!gotMsg) + { + PR_snprintf(msg, sizeof(msg), "Error 0x%x in module 0x%x", + NS_ERROR_GET_CODE(r), NS_ERROR_GET_MODULE(r)); + } + PyObject *evalue = Py_BuildValue("is", r, msg); + return evalue; +} +#endif + +PyObject *PyXPCOM_BuildPyException(nsresult r) +{ +#ifndef VBOX + // Need the message etc. + PyObject *evalue = Py_BuildValue("i", r); +#else + PyObject *evalue = PyXPCOM_BuildErrorMessage(r); +#endif + PyErr_SetObject(PyXPCOM_Error, evalue); + Py_XDECREF(evalue); + return NULL; +} + +nsresult PyXPCOM_SetCOMErrorFromPyException() +{ + if (!PyErr_Occurred()) + // No error occurred + return NS_OK; + nsresult rv = NS_ERROR_FAILURE; + if (PyErr_ExceptionMatches(PyExc_MemoryError)) + rv = NS_ERROR_OUT_OF_MEMORY; + // todo: + // * Set an exception using the exception service. + + // Once we have returned to the xpcom caller, we don't want to leave a + // Python exception pending - it may get noticed when the next call + // is made on the same thread. + PyErr_Clear(); + return rv; +} + +/* Obtains a string from a Python traceback. + This is the exact same string as "traceback.print_exc" would return. + + Pass in a Python traceback object (probably obtained from PyErr_Fetch()) + Result is a string which must be free'd using PyMem_Free() +*/ +#define TRACEBACK_FETCH_ERROR(what) {errMsg = what; goto done;} + +char *PyTraceback_AsString(PyObject *exc_tb) +{ + const char *errMsg = NULL; /* holds a local error message */ + char *result = NULL; /* a valid, allocated result. */ + PyObject *modStringIO = NULL; + PyObject *modTB = NULL; + PyObject *obFuncStringIO = NULL; + PyObject *obStringIO = NULL; + PyObject *obFuncTB = NULL; + PyObject *argsTB = NULL; + PyObject *obResult = NULL; + +#if PY_MAJOR_VERSION <= 2 + /* Import the modules we need - cStringIO and traceback */ + modStringIO = PyImport_ImportModule("cStringIO"); + if (modStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant import cStringIO\n"); + + modTB = PyImport_ImportModule("traceback"); + if (modTB==NULL) + TRACEBACK_FETCH_ERROR("cant import traceback\n"); + /* Construct a cStringIO object */ + obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO"); + if (obFuncStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant find cStringIO.StringIO\n"); + obStringIO = PyObject_CallObject(obFuncStringIO, NULL); + if (obStringIO==NULL) + TRACEBACK_FETCH_ERROR("cStringIO.StringIO() failed\n"); +#else + /* Import the modules we need - io and traceback */ + modStringIO = PyImport_ImportModule("io"); + if (modStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant import io\n"); + + modTB = PyImport_ImportModule("traceback"); + if (modTB==NULL) + TRACEBACK_FETCH_ERROR("cant import traceback\n"); + /* Construct a StringIO object */ + obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO"); + if (obFuncStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant find io.StringIO\n"); + obStringIO = PyObject_CallObject(obFuncStringIO, NULL); + if (obStringIO==NULL) + TRACEBACK_FETCH_ERROR("io.StringIO() failed\n"); +#endif + /* Get the traceback.print_exception function, and call it. */ + obFuncTB = PyObject_GetAttrString(modTB, "print_tb"); + if (obFuncTB==NULL) + TRACEBACK_FETCH_ERROR("cant find traceback.print_tb\n"); + + argsTB = Py_BuildValue("OOO", + exc_tb ? exc_tb : Py_None, + Py_None, + obStringIO); + if (argsTB==NULL) + TRACEBACK_FETCH_ERROR("cant make print_tb arguments\n"); + + obResult = PyObject_CallObject(obFuncTB, argsTB); + if (obResult==NULL) + TRACEBACK_FETCH_ERROR("traceback.print_tb() failed\n"); + /* Now call the getvalue() method in the StringIO instance */ + Py_DECREF(obFuncStringIO); + obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue"); + if (obFuncStringIO==NULL) + TRACEBACK_FETCH_ERROR("cant find getvalue function\n"); + Py_DECREF(obResult); + obResult = PyObject_CallObject(obFuncStringIO, NULL); + if (obResult==NULL) + TRACEBACK_FETCH_ERROR("getvalue() failed.\n"); + + /* And it should be a string all ready to go - duplicate it. */ +#if PY_MAJOR_VERSION <= 2 + if (!PyString_Check(obResult)) +#else + if (!PyUnicode_Check(obResult)) +#endif + TRACEBACK_FETCH_ERROR("getvalue() did not return a string\n"); + + { // a temp scope so I can use temp locals. +#if PY_MAJOR_VERSION <= 2 + char *tempResult = PyString_AsString(obResult); +#else + /* PyUnicode_AsUTF8() is const char * as of Python 3.7, char * earlier. */ + const char *tempResult = (const char *)PyUnicode_AsUTF8(obResult); +#endif + result = (char *)PyMem_Malloc(strlen(tempResult)+1); + if (result==NULL) + TRACEBACK_FETCH_ERROR("memory error duplicating the traceback string\n"); + + strcpy(result, tempResult); + } // end of temp scope. +done: + /* All finished - first see if we encountered an error */ + if (result==NULL && errMsg != NULL) { + result = (char *)PyMem_Malloc(strlen(errMsg)+1); + if (result != NULL) + /* if it does, not much we can do! */ + strcpy(result, errMsg); + } + Py_XDECREF(modStringIO); + Py_XDECREF(modTB); + Py_XDECREF(obFuncStringIO); + Py_XDECREF(obStringIO); + Py_XDECREF(obFuncTB); + Py_XDECREF(argsTB); + Py_XDECREF(obResult); + return result; +} + +// See comments in PyXPCOM.h for why we need this! +void PyXPCOM_MakePendingCalls() +{ + while (1) { + int rc = Py_MakePendingCalls(); + if (rc == 0) + break; + // An exception - just report it as normal. + // Note that a traceback is very unlikely! + PyXPCOM_LogError("Unhandled exception detected before entering Python.\n"); + PyErr_Clear(); + // And loop around again until we are told everything is done! + } +} |