added exception detection

This commit is contained in:
roberto@precise64
2012-03-18 08:21:45 +01:00
parent 9001af1beb
commit f9f9f25a01
11 changed files with 160 additions and 28 deletions
+3
View File
@@ -1563,6 +1563,9 @@ nextlock:
else if (WIFEXITED(waitpid_status) && WEXITSTATUS(waitpid_status) == UWSGI_DE_HIJACKED_CODE) {
uwsgi_log("...restoring worker %d (pid: %d)...\n", uwsgi.mywid, (int) diedpid);
}
else if (WIFEXITED(waitpid_status) && WEXITSTATUS(waitpid_status) == UWSGI_EXCEPTION_CODE) {
uwsgi_log("... monitored exception detected, respawning worker %d (pid: %d)...\n", uwsgi.mywid, (int) diedpid);
}
else if (WIFEXITED(waitpid_status) && WEXITSTATUS(waitpid_status) == UWSGI_QUIET_CODE) {
// noop
}
-6
View File
@@ -375,12 +375,6 @@ int uwsgi_response_subhandler_pump(struct wsgi_request *wsgi_req) {
return UWSGI_AGAIN;
clear:
if (wsgi_req->async_input) {
Py_DECREF((PyObject *)wsgi_req->async_input);
}
if (wsgi_req->async_environ) {
PyDict_Clear(wsgi_req->async_environ);
}
Py_XDECREF((PyObject *)wsgi_req->async_placeholder);
Py_DECREF((PyObject *)wsgi_req->async_result);
-2
View File
@@ -108,8 +108,6 @@ struct uwsgi_option uwsgi_python_options[] = {
{"ini-paste", required_argument, 0, "load a paste.deploy config file containing uwsgi section", uwsgi_opt_ini_paste, NULL, UWSGI_OPT_IMMEDIATE},
{"ini-paste-logged", required_argument, 0, "load a paste.deploy config file containing uwsgi section (load loggers too)", uwsgi_opt_ini_paste, NULL, UWSGI_OPT_IMMEDIATE},
#endif
{"catch-exceptions", no_argument, 0, "report exception has http output (discouraged)", uwsgi_opt_true, &up.catch_exceptions, 0},
{"ignore-script-name", no_argument, 0, "ignore SCRIPT_NAME", uwsgi_opt_true, &up.ignore_script_name, 0},
{"reload-os-env", no_argument, 0, "force reload of os.environ at each request", uwsgi_opt_true, &up.reload_os_env, 0},
#ifndef UWSGI_PYPY
{"no-site", no_argument, 0, "do not import site module", uwsgi_opt_true, &Py_NoSiteFlag, 0},
+88 -1
View File
@@ -8,20 +8,103 @@ int manage_python_response(struct wsgi_request *wsgi_req) {
return uwsgi_response_subhandler_wsgi(wsgi_req);
}
char *uwsgi_python_get_exception_type(PyObject *exc) {
char *class_name = NULL;
if (PyClass_Check(exc)) {
class_name = PyString_AsString( ((PyClassObject*)(exc))->cl_name );
}
else {
class_name = (char *) ((PyTypeObject*)exc)->tp_name;
}
if (class_name) {
char *dot = strrchr(class_name, '.');
if (dot) class_name = dot+1;
PyObject *module_name = PyObject_GetAttrString(exc, "__module__");
if (module_name) {
char *mod_name = PyString_AsString(module_name);
if (mod_name && strcmp(mod_name, "exceptions") ) {
char *ret = uwsgi_concat3(mod_name, ".", class_name);
Py_DECREF(module_name);
return ret;
}
Py_DECREF(module_name);
return uwsgi_str(class_name);
}
}
return NULL;
}
char *uwsgi_python_get_exception_value(PyObject *value) {
return PyString_AsString( PyObject_Str(value) );
}
char *uwsgi_python_get_exception_repr(PyObject *exc, PyObject *value) {
char *exc_type = uwsgi_python_get_exception_type(exc);
char *exc_value = uwsgi_python_get_exception_value(value);
if (exc_type && exc_value) {
return uwsgi_concat3(exc_type, ": ", exc_value);
}
return NULL;
}
int uwsgi_python_manage_exceptions(void) {
PyObject *type = NULL;
PyObject *value = NULL;
PyObject *traceback = NULL;
char *exc_type = NULL;
char *exc_value = NULL;
char *exc_repr = NULL;
PyErr_Fetch(&type, &value, &traceback);
PyErr_NormalizeException(&type, &value, &traceback);
if (uwsgi.reload_on_exception_type) {
exc_type = uwsgi_python_get_exception_type(type);
}
if (uwsgi.reload_on_exception_value) {
exc_value = uwsgi_python_get_exception_value(value);
}
if (uwsgi.reload_on_exception_repr) {
exc_repr = uwsgi_python_get_exception_repr(type, value);
}
int ret = uwsgi_manage_exception(exc_type, exc_value, exc_repr);
// free memory allocated for strcmp
if (exc_type) free(exc_type);
if (exc_repr) free(exc_repr);
PyErr_Restore(type, value, traceback);
return ret;
}
PyObject *python_call(PyObject *callable, PyObject *args, int catch, struct wsgi_request *wsgi_req) {
PyObject *pyret;
//uwsgi_log("ready to call %p %p\n", callable, args);
pyret = PyEval_CallObject(callable, args);
pyret = PyEval_CallObject(callable, args);
//uwsgi_log("called\n");
if (PyErr_Occurred()) {
int do_exit = uwsgi_python_manage_exceptions();
if (PyErr_ExceptionMatches(PyExc_MemoryError)) {
uwsgi_log("Memory Error detected !!!\n");
}
// this can be in a spooler or in the master
if (uwsgi.mywid > 0) {
uwsgi.workers[uwsgi.mywid].exceptions++;
@@ -32,6 +115,10 @@ PyObject *python_call(PyObject *callable, PyObject *args, int catch, struct wsgi
if (!catch) {
PyErr_Print();
}
if (do_exit) {
exit(UWSGI_EXCEPTION_CODE);
}
}
#ifdef UWSGI_DEBUG
-3
View File
@@ -136,9 +136,6 @@ struct uwsgi_python {
char *callable;
int ignore_script_name;
int catch_exceptions;
int *current_recursion_depth;
struct _frame **current_frame;
-6
View File
@@ -225,12 +225,6 @@ int uwsgi_response_subhandler_web3(struct wsgi_request *wsgi_req) {
return UWSGI_AGAIN;
clear:
if (wsgi_req->async_input) {
Py_DECREF((PyObject *)wsgi_req->async_input);
}
if (wsgi_req->async_environ) {
PyDict_Clear(wsgi_req->async_environ);
}
Py_XDECREF((PyObject *)wsgi_req->async_placeholder);
Py_DECREF((PyObject *)wsgi_req->async_result);
+10 -2
View File
@@ -363,7 +363,7 @@ int uwsgi_request_wsgi(struct wsgi_request *wsgi_req) {
if (wsgi_req->appid_len == 0) {
if (!up.ignore_script_name) {
if (!uwsgi.ignore_script_name) {
wsgi_req->appid = wsgi_req->script_name;
wsgi_req->appid_len = wsgi_req->script_name_len;
}
@@ -466,7 +466,7 @@ int uwsgi_request_wsgi(struct wsgi_request *wsgi_req) {
}
else if (up.catch_exceptions) {
else if (uwsgi.catch_exceptions) {
// LOCK THIS PART
@@ -501,6 +501,14 @@ int uwsgi_request_wsgi(struct wsgi_request *wsgi_req) {
close(tmp_stderr);
}
// this object must be freed/cleared always
if (wsgi_req->async_input) {
Py_DECREF((PyObject *)wsgi_req->async_input);
}
if (wsgi_req->async_environ) {
PyDict_Clear(wsgi_req->async_environ);
}
clear:
up.reset_ts(wsgi_req, wi);
+1 -7
View File
@@ -159,7 +159,7 @@ void *uwsgi_request_subhandler_wsgi(struct wsgi_request *wsgi_req, struct uwsgi_
// call
PyTuple_SetItem(wsgi_req->async_args, 0, wsgi_req->async_environ);
return python_call(wsgi_req->async_app, wsgi_req->async_args, up.catch_exceptions, wsgi_req);
return python_call(wsgi_req->async_app, wsgi_req->async_args, uwsgi.catch_exceptions, wsgi_req);
}
int uwsgi_response_subhandler_wsgi(struct wsgi_request *wsgi_req) {
@@ -280,12 +280,6 @@ clear:
if (wsgi_req->sendfile_fd != -1) {
Py_DECREF((PyObject *)wsgi_req->async_sendfile);
}
if (wsgi_req->async_input) {
Py_DECREF((PyObject *)wsgi_req->async_input);
}
if (wsgi_req->async_environ) {
PyDict_Clear(wsgi_req->async_environ);
}
Py_XDECREF((PyObject *)wsgi_req->async_placeholder);
clear2:
Py_DECREF((PyObject *)wsgi_req->async_result);
+43
View File
@@ -4161,3 +4161,46 @@ void uwsgi_write_pidfile(char *pidfile_name) {
}
fclose(pidfile);
}
int uwsgi_manage_exception(char *type, char *value, char *repr) {
struct uwsgi_string_list *list = NULL;
// first manage non fatal case (like signals and alarm)....
if (uwsgi.reload_on_exception) {
return -1;
}
if (type) {
list = uwsgi.reload_on_exception_type;
while(list) {
if (!strcmp(list->value, type)) {
return -1;
}
list = list->next;
}
}
if (value) {
list = uwsgi.reload_on_exception_value;
while(list) {
if (!strcmp(list->value, value)) {
return -1;
}
list = list->next;
}
}
if (repr) {
list = uwsgi.reload_on_exception_repr;
while(list) {
if (!strcmp(list->value, repr)) {
return -1;
}
list = list->next;
}
}
return 0;
}
+6
View File
@@ -258,6 +258,12 @@ static struct uwsgi_option uwsgi_base_options[] = {
{"upload-progress", required_argument, 0, "enable creation of .json files in the specified directory during a file upload", uwsgi_opt_set_str, &uwsgi.upload_progress,0},
{"no-default-app", no_argument, 0, "do not fallback to default app", uwsgi_opt_true, &uwsgi.no_default_app, 0},
{"manage-script-name", no_argument, 0, "automatically rewrite SCRIPT_NAME and PATH_INFO", uwsgi_opt_true, &uwsgi.manage_script_name, 0},
{"ignore-script-name", no_argument, 0, "ignore SCRIPT_NAME", uwsgi_opt_true, &uwsgi.ignore_script_name, 0},
{"catch-exceptions", no_argument, 0, "report exception has http output (discouraged)", uwsgi_opt_true, &uwsgi.catch_exceptions, 0},
{"reload-on-exception", no_argument, 0, "reload a worker when an exception is raised", uwsgi_opt_true, &uwsgi.reload_on_exception, 0},
{"reload-on-exception-type", no_argument, 0, "reload a worker when a specific exception type is raised", uwsgi_opt_add_string_list, &uwsgi.reload_on_exception_type, 0},
{"reload-on-exception-value", no_argument, 0, "reload a worker when a specific exception value is raised", uwsgi_opt_add_string_list, &uwsgi.reload_on_exception_value, 0},
{"reload-on-exception-repr", no_argument, 0, "reload a worker when a specific exception type+value (language-specific) is raised", uwsgi_opt_add_string_list, &uwsgi.reload_on_exception_repr, 0},
#ifdef UWSGI_UDP
{"udp", required_argument, 0, "run the udp server on the specified address", uwsgi_opt_set_str, &uwsgi.udp_socket, UWSGI_OPT_MASTER},
#endif
+9 -1
View File
@@ -548,6 +548,7 @@ struct uwsgi_opt {
#define UWSGI_EXILE_CODE 26
#define UWSGI_FAILED_APP_CODE 22
#define UWSGI_DE_HIJACKED_CODE 173
#define UWSGI_EXCEPTION_CODE 5
#define UWSGI_QUIET_CODE 29
#define MAX_VARS 64
@@ -1205,6 +1206,13 @@ struct uwsgi_server {
int ignore_script_name;
int manage_script_name;
int reload_on_exception;
int catch_exceptions;
struct uwsgi_string_list *reload_on_exception_type;
struct uwsgi_string_list *reload_on_exception_value;
struct uwsgi_string_list *reload_on_exception_repr;
int no_default_app;
// exit if no-app is loaded
int need_app;
@@ -1448,7 +1456,6 @@ struct uwsgi_server {
char *chdir;
char *chdir2;
int catch_exceptions;
int vacuum;
int no_server;
@@ -2754,6 +2761,7 @@ void uwsgi_setup_post_buffering(void);
struct uwsgi_lock_item *uwsgi_lock_ipcsem_init(char *);
void uwsgi_write_pidfile(char *);
int uwsgi_manage_exception(char *, char *, char *);
#ifdef UWSGI_AS_SHARED_LIBRARY
int uwsgi_init(int, char **, char **);