From 9fe275141fbe3d49da828284d61ae26126fd433b Mon Sep 17 00:00:00 2001 From: "roberto@precise64" Date: Fri, 11 May 2012 20:44:56 +0200 Subject: [PATCH] added preliminary python tracebacker --- plugins/python/python_plugin.c | 145 ++++++++++++++++++++++++++++--- plugins/python/uwsgi_python.h | 2 + plugins/python/wsgi_headers.c | 16 ++++ plugins/python/wsgi_subhandler.c | 2 + uwsgi.h | 1 + 5 files changed, 152 insertions(+), 14 deletions(-) diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index c05686a5..868a659a 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -120,6 +120,7 @@ struct uwsgi_option uwsgi_python_options[] = { {"pyrun", required_argument, 0, "run a python script in the uWSGI environment", uwsgi_opt_pyrun, NULL, 0}, #ifdef UWSGI_THREADING + {"py-tracebacker", required_argument, 0, "enable the uWSGI python tracebacker", uwsgi_opt_set_str, &up.tracebacker, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"py-auto-reload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"py-autoreload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, {"python-auto-reload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, @@ -323,6 +324,11 @@ void uwsgi_python_post_fork() { pthread_t par_tid; pthread_create(&par_tid, NULL, uwsgi_python_autoreloader_thread, NULL); } + if (up.tracebacker) { + // spawn the tracebacker thread + pthread_t ptb_tid; + pthread_create(&ptb_tid, NULL, uwsgi_python_tracebacker_thread, NULL); + } #endif } @@ -1224,25 +1230,25 @@ int uwsgi_check_python_mtime(PyObject *times_dict, char *filename) { } return 0; } -void *uwsgi_python_autoreloader_thread(void *foobar) { - - PyObject *modules; +PyObject *uwsgi_python_setup_thread(char *name) { // block signals on this thread - sigset_t smask; + sigset_t smask; sigfillset(&smask); #ifndef UWSGI_DEBUG sigdelset(&smask, SIGSEGV); #endif pthread_sigmask(SIG_BLOCK, &smask, NULL); - PyThreadState *pts = PyThreadState_New(up.main_thread->interp); - pthread_setspecific(up.upt_save_key, (void *) pts); + PyThreadState *pts = PyThreadState_New(up.main_thread->interp); + pthread_setspecific(up.upt_save_key, (void *) pts); pthread_setspecific(up.upt_gil_key, (void *) pts); - UWSGI_GET_GIL; - PyObject *threading_module = PyImport_ImportModule("threading"); - if (threading_module) { - PyObject *threading_module_dict = PyModule_GetDict(threading_module); + + UWSGI_GET_GIL; + + PyObject *threading_module = PyImport_ImportModule("threading"); + if (threading_module) { + PyObject *threading_module_dict = PyModule_GetDict(threading_module); if (threading_module_dict) { #ifdef PYTHREE PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "current_thread"); @@ -1256,20 +1262,131 @@ void *uwsgi_python_autoreloader_thread(void *foobar) { PyErr_Clear(); } else { - PyObject_SetAttrString(current_thread, "name", PyString_FromString("uWSGIAutoReloader")); + PyObject_SetAttrString(current_thread, "name", PyString_FromString(name)); Py_INCREF(current_thread); - modules = PyImport_GetModuleDict(); - goto cycle; + return current_thread; } } } + } + + return NULL; +} + +void *uwsgi_python_tracebacker_thread(void *foobar) { + + struct iovec iov[9]; + + PyObject *new_thread = uwsgi_python_setup_thread("uWSGITraceBacker"); + if (!new_thread) return NULL; + + struct sockaddr_un so_sun; + socklen_t so_sun_len = 0; + + char *str_wid = uwsgi_num2str(uwsgi.mywid); + char *sock_path = uwsgi_concat2(up.tracebacker, str_wid); + + int current_defer_accept = uwsgi.no_defer_accept; + uwsgi.no_defer_accept = 1; + int fd = bind_to_unix(sock_path, uwsgi.listen_queue, uwsgi.chmod_socket, uwsgi.abstract_socket); + uwsgi.no_defer_accept = current_defer_accept; + PyObject *threading_module = PyImport_ImportModule("threading"); + if (!threading_module) return NULL; + //PyObject *threading_dict = PyModule_GetDict(threading_module); + + PyObject *traceback_module = PyImport_ImportModule("traceback"); + if (!traceback_module) return NULL; + PyObject *traceback_dict = PyModule_GetDict(traceback_module); + PyObject *extract_stack = PyDict_GetItemString(traceback_dict, "extract_stack"); + + PyObject *sys_module = PyImport_ImportModule("sys"); + PyObject *sys_dict = PyModule_GetDict(sys_module); + + + PyObject *_current_frames = PyDict_GetItemString(sys_dict, "_current_frames"); + + uwsgi_log("python tracebacker for worker %d available on %s\n", uwsgi.mywid, sock_path); + for(;;) { + UWSGI_RELEASE_GIL; + int client_fd = accept(fd, (struct sockaddr *) &so_sun, &so_sun_len); + if (client_fd < 0) { + uwsgi_error("accept()"); + UWSGI_GET_GIL; + continue; + } + UWSGI_GET_GIL; +// here is the core of the tracebacker + PyObject *current_frames = PyEval_CallObject(_current_frames, (PyObject *)NULL); + if (!current_frames) goto end; + uwsgi_log("current_frames = %p\n", current_frames); + PyObject *current_frames_items = PyObject_GetAttrString(current_frames, "items"); + if (!current_frames_items) goto end; + uwsgi_log("current_frames_items = %p\n", current_frames_items); + PyObject *frames_ret = PyEval_CallObject(current_frames_items, (PyObject *)NULL); + if (!frames_ret) goto end; + uwsgi_log("frames_ret = %p\n", frames_ret); + PyObject *frames_iter = PyObject_GetIter(frames_ret); + uwsgi_log("frames_iter = %p\n", frames_iter); + PyObject *frame = PyIter_Next(frames_iter); + while(frame) { + uwsgi_log("frame = %p\n", frame); + PyObject *stack = PyTuple_GetItem(frame, 1); + uwsgi_log("stack = %p\n", stack); + PyObject *arg_tuple = PyTuple_New(1); + PyTuple_SetItem(arg_tuple, 0, stack); + PyObject *stacktrace = PyEval_CallObject( extract_stack, arg_tuple); + uwsgi_log("stacktrace = %p\n", stacktrace); + PyObject *stacktrace_iter = PyObject_GetIter(stacktrace); + PyObject *st_items = PyIter_Next(stacktrace_iter); + while(st_items) { + uwsgi_log("st_items = %p\n", st_items); + PyObject *st_filename = PyTuple_GetItem(st_items, 0); + PyObject *st_lineno = PyTuple_GetItem(st_items, 1); + PyObject *st_name = PyTuple_GetItem(st_items, 2); + PyObject *st_line = PyTuple_GetItem(st_items, 3); + iov[0].iov_base = "filename = "; + iov[0].iov_len = 11; + iov[1].iov_base = PyString_AsString(st_filename); + iov[1].iov_len = strlen(iov[1].iov_base); + iov[2].iov_base = " lineno = "; + iov[2].iov_len = 10 ; + iov[3].iov_base = uwsgi_num2str(PyInt_AsLong(st_lineno)); + iov[3].iov_len = strlen(iov[3].iov_base); + iov[4].iov_base = " function = "; + iov[4].iov_len = 12 ; + iov[5].iov_base = PyString_AsString(st_name); + iov[5].iov_len = strlen(iov[5].iov_base); + iov[6].iov_base = "\n"; + iov[6].iov_len = 1 ; + if (st_line) { + } + if (writev(client_fd, iov, 7) < 0) { + uwsgi_error("writev()"); + } + st_items = PyIter_Next(stacktrace_iter); + } + frame = PyIter_Next(frames_iter); + } + + +end: + close(client_fd); } return NULL; -cycle: +} + +void *uwsgi_python_autoreloader_thread(void *foobar) { + + PyObject *new_thread = uwsgi_python_setup_thread("uWSGIAutoReloader"); + if (!new_thread) return NULL; + + PyObject *modules = PyImport_GetModuleDict(); + if (uwsgi.mywid == 1) { uwsgi_log("Python auto-reloader enabled\n"); } + PyObject *times_dict = PyDict_New(); char *filename; for(;;) { diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 7c8aaac4..6424c1d0 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -160,6 +160,7 @@ struct uwsgi_python { void (*gil_get) (void); void (*gil_release) (void); int auto_reload; + char *tracebacker; struct uwsgi_string_list *auto_reload_ignore; #endif @@ -265,6 +266,7 @@ void uwsgi_python_reset_random_seed(void); char *uwsgi_pythonize(char *); void *uwsgi_python_autoreloader_thread(void *); +void *uwsgi_python_tracebacker_thread(void *); int uwsgi_python_manage_exceptions(void); int uwsgi_python_do_send_headers(struct wsgi_request *); diff --git a/plugins/python/wsgi_headers.c b/plugins/python/wsgi_headers.c index 886a18e3..71205b86 100644 --- a/plugins/python/wsgi_headers.c +++ b/plugins/python/wsgi_headers.c @@ -30,6 +30,11 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) { wsgi_req->status_header = NULL; } + if (wsgi_req->headers) { + Py_DECREF((PyObject *)wsgi_req->headers); + wsgi_req->headers = NULL; + } + // this must be done before headers management if (PyTuple_Size(args) > 2) { exc_info = PyTuple_GetItem(args, 2); @@ -129,6 +134,8 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) { return PyErr_Format(PyExc_TypeError, "start_response() takes at least 2 arguments"); } + wsgi_req->headers = headers; + Py_INCREF((PyObject *)wsgi_req->headers); if (!PyList_Check(headers)) { return PyErr_Format(PyExc_TypeError, "http headers must be in a python list"); @@ -246,6 +253,10 @@ PyObject *py_uwsgi_spit(PyObject * self, PyObject * args) { int uwsgi_python_do_send_headers(struct wsgi_request *wsgi_req) { + uwsgi_log("- headers = %d\n", ((PyObject *)wsgi_req->headers)->ob_refcnt); + uwsgi_log("- status = %p\n", wsgi_req->status_header); + uwsgi_log("- status = %d\n", ((PyObject *)wsgi_req->status_header)->ob_refcnt); + #ifdef __sun__ int remains = wsgi_req->headers_hvec + 1; int iov_size; @@ -274,6 +285,11 @@ int uwsgi_python_do_send_headers(struct wsgi_request *wsgi_req) { wsgi_req->status_header = NULL; } + if (wsgi_req->headers) { + Py_DECREF((PyObject *)wsgi_req->headers); + wsgi_req->headers = NULL; + } + if (wsgi_req->write_errors > uwsgi.write_errors_tolerance && !uwsgi.disable_write_exception) { uwsgi_py_write_set_exception(wsgi_req); return -1; diff --git a/plugins/python/wsgi_subhandler.c b/plugins/python/wsgi_subhandler.c index 62b9f9af..7f480fe8 100644 --- a/plugins/python/wsgi_subhandler.c +++ b/plugins/python/wsgi_subhandler.c @@ -297,9 +297,11 @@ clear2: uwsgi_python_do_send_headers(wsgi_req); } + Py_DECREF((PyObject *)wsgi_req->async_result); PyErr_Clear(); + return UWSGI_OK; } diff --git a/uwsgi.h b/uwsgi.h index b8f9d503..402c2200 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -897,6 +897,7 @@ struct wsgi_request { int status; void *status_header; + void *headers; size_t response_size; ssize_t headers_size;