From c2091c52eb19857e328c75fdd45179858bd6fca5 Mon Sep 17 00:00:00 2001 From: "roberto@quantal64" Date: Sun, 3 Jun 2012 10:58:34 +0200 Subject: [PATCH] completed the python tracebacker --- plugins/python/python_plugin.c | 105 +---------------- plugins/python/tracebacker.c | 202 +++++++++++++++++++++++++++++++++ plugins/python/uwsgi_python.h | 2 + plugins/python/uwsgiplugin.py | 2 +- uwsgi.c | 21 ++++ uwsgi.h | 1 + 6 files changed, 231 insertions(+), 102 deletions(-) create mode 100644 plugins/python/tracebacker.c diff --git a/plugins/python/python_plugin.c b/plugins/python/python_plugin.c index 66c10499..a98bacd0 100644 --- a/plugins/python/python_plugin.c +++ b/plugins/python/python_plugin.c @@ -120,7 +120,9 @@ 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 +#ifndef UWSGI_PYPY {"py-tracebacker", required_argument, 0, "enable the uWSGI python tracebacker", uwsgi_opt_set_str, &up.tracebacker, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER}, +#endif {"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}, @@ -330,11 +332,13 @@ void uwsgi_python_post_fork() { pthread_t par_tid; pthread_create(&par_tid, NULL, uwsgi_python_autoreloader_thread, NULL); } +#ifndef UWSGI_PYPY if (up.tracebacker) { // spawn the tracebacker thread pthread_t ptb_tid; pthread_create(&ptb_tid, NULL, uwsgi_python_tracebacker_thread, NULL); } +#endif #endif } @@ -1344,107 +1348,6 @@ PyObject *uwsgi_python_setup_thread(char *name) { 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; -} void *uwsgi_python_autoreloader_thread(void *foobar) { diff --git a/plugins/python/tracebacker.c b/plugins/python/tracebacker.c new file mode 100644 index 00000000..aeefc125 --- /dev/null +++ b/plugins/python/tracebacker.c @@ -0,0 +1,202 @@ +#include "uwsgi_python.h" + +extern struct uwsgi_server uwsgi; +extern struct uwsgi_python up; + + +char *uwsgi_python_get_thread_name(PyObject *thread_id) { + PyObject *threading_module = PyImport_ImportModule("threading"); + if (!threading_module) return NULL; + + PyObject *threading_dict = PyModule_GetDict(threading_module); + if (!threading_dict) return NULL; + + PyObject *threading_enumerate = PyDict_GetItemString(threading_dict, "enumerate"); + if (!threading_enumerate) return NULL; + + PyObject *threads_list = PyEval_CallObject(threading_enumerate, (PyObject *)NULL); + if (!threads_list) return NULL; + + PyObject *threads_list_iter = PyObject_GetIter(threads_list); + if (!threads_list_iter) goto clear; + + PyObject *threads_list_next = PyIter_Next(threads_list_iter); + while(threads_list_next) { + PyObject *thread_ident = PyObject_GetAttrString(threads_list_next, "ident"); + if (!thread_ident) goto clear2; + if (PyInt_AsLong(thread_ident) == PyInt_AsLong(thread_id)) { + PyObject *thread_name = PyObject_GetAttrString(threads_list_next, "name"); + if (!thread_name) goto clear2; + char *name = PyString_AsString(thread_name); + Py_DECREF(threads_list_iter); + Py_DECREF(threads_list); + return name; + } + threads_list_next = PyIter_Next(threads_list_iter); + } + +clear2: + Py_DECREF(threads_list_iter); +clear: + Py_DECREF(threads_list); + return NULL; +} + +void *uwsgi_python_tracebacker_thread(void *foobar) { + + struct iovec iov[11]; + + 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 *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 end2; + + PyObject *current_frames_items = PyObject_GetAttrString(current_frames, "items"); + if (!current_frames_items) goto end; + + PyObject *frames_ret = PyEval_CallObject(current_frames_items, (PyObject *)NULL); + if (!frames_ret) goto end3; + + PyObject *frames_iter = PyObject_GetIter(frames_ret); + if (!frames_iter) goto end4; + + + // we have the first frame, lets parse it + if (write(client_fd, "*** uWSGI Python tracebacker output ***\n\n", 41) < 0) { + uwsgi_error("write()"); + } + PyObject *frame = PyIter_Next(frames_iter); + while(frame) { + + PyObject *thread_id = PyTuple_GetItem(frame, 0); + if (!thread_id) goto next2; + + PyObject *stack = PyTuple_GetItem(frame, 1); + if (!stack) goto next2; + + PyObject *arg_tuple = PyTuple_New(1); + PyTuple_SetItem(arg_tuple, 0, stack); + PyObject *stacktrace = PyEval_CallObject( extract_stack, arg_tuple); + Py_DECREF(arg_tuple); + if (!stacktrace) goto next2; + + PyObject *stacktrace_iter = PyObject_GetIter(stacktrace); + if (!stacktrace_iter) goto next2; + + PyObject *st_items = PyIter_Next(stacktrace_iter); + // we have the first traceback item + while(st_items) { + PyObject *st_filename = PyTuple_GetItem(st_items, 0); + if (!st_filename) goto next; + PyObject *st_lineno = PyTuple_GetItem(st_items, 1); + if (!st_lineno) goto next; + PyObject *st_name = PyTuple_GetItem(st_items, 2); + if (!st_name) goto next; + + PyObject *st_line = PyTuple_GetItem(st_items, 3); + + iov[0].iov_base = "thread_id = "; + iov[0].iov_len = 12; + + iov[1].iov_base = uwsgi_python_get_thread_name(thread_id); + if (!iov[1].iov_base) { + iov[1].iov_base = ""; + } + iov[1].iov_len = strlen(iov[1].iov_base); + + iov[2].iov_base = " filename = "; + iov[2].iov_len = 12; + + iov[3].iov_base = PyString_AsString(st_filename); + iov[3].iov_len = strlen(iov[3].iov_base); + + iov[4].iov_base = " lineno = "; + iov[4].iov_len = 10 ; + + iov[5].iov_base = uwsgi_num2str(PyInt_AsLong(st_lineno)); + iov[5].iov_len = strlen(iov[5].iov_base); + + iov[6].iov_base = " function = "; + iov[6].iov_len = 12 ; + + iov[7].iov_base = PyString_AsString(st_name); + iov[7].iov_len = strlen(iov[7].iov_base); + + iov[8].iov_base = ""; + iov[8].iov_len = 0 ; + + iov[9].iov_base = ""; + iov[9].iov_len = 0; + + iov[10].iov_base = "\n"; + iov[10].iov_len = 1; + + if (st_line) { + iov[8].iov_base = " line = "; + iov[8].iov_len = 8; + iov[9].iov_base = PyString_AsString(st_line); + iov[9].iov_len = strlen(iov[9].iov_base); + } + + if (writev(client_fd, iov, 11) < 0) { + uwsgi_error("writev()"); + } + + // free the line_no + free(iov[5].iov_base); + st_items = PyIter_Next(stacktrace_iter); + } + if (write(client_fd, "\n", 1) < 0) { + uwsgi_error("write()"); + } +next: + Py_DECREF(stacktrace_iter); +next2: + frame = PyIter_Next(frames_iter); + } + +end4: + Py_DECREF(frames_ret); +end3: + Py_DECREF(current_frames_items); +end: + Py_DECREF(current_frames); +end2: + close(client_fd); + } + return NULL; +} diff --git a/plugins/python/uwsgi_python.h b/plugins/python/uwsgi_python.h index 6ad5604c..98b642fe 100644 --- a/plugins/python/uwsgi_python.h +++ b/plugins/python/uwsgi_python.h @@ -275,6 +275,8 @@ void *uwsgi_python_tracebacker_thread(void *); int uwsgi_python_manage_exceptions(void); int uwsgi_python_do_send_headers(struct wsgi_request *); +void *uwsgi_python_tracebacker_thread(void *); +PyObject *uwsgi_python_setup_thread(char *); #ifdef UWSGI_PYPY #undef UWSGI_MINTERPRETERS diff --git a/plugins/python/uwsgiplugin.py b/plugins/python/uwsgiplugin.py index 4b46e88f..887b5ba5 100644 --- a/plugins/python/uwsgiplugin.py +++ b/plugins/python/uwsgiplugin.py @@ -3,7 +3,7 @@ import os,sys from distutils import sysconfig NAME='python' -GCC_LIST = ['python_plugin', 'pyutils', 'pyloader', 'wsgi_handlers', 'wsgi_headers', 'wsgi_subhandler', 'web3_subhandler', 'pump_subhandler', 'gil', 'uwsgi_pymodule', 'profiler', 'symimporter'] +GCC_LIST = ['python_plugin', 'pyutils', 'pyloader', 'wsgi_handlers', 'wsgi_headers', 'wsgi_subhandler', 'web3_subhandler', 'pump_subhandler', 'gil', 'uwsgi_pymodule', 'profiler', 'symimporter', 'tracebacker'] #OBJ_LIST = ['/usr/lib/libpython2.6.a'] CFLAGS = ['-I' + sysconfig.get_python_inc(), '-I' + sysconfig.get_python_inc(plat_specific=True) ] diff --git a/uwsgi.c b/uwsgi.c index ffde9391..05ef2250 100644 --- a/uwsgi.c +++ b/uwsgi.c @@ -100,6 +100,9 @@ static struct uwsgi_option uwsgi_base_options[] = { {"pause", required_argument, 0, "pause an instance", uwsgi_opt_pidfile_signal, (void *) SIGTSTP, UWSGI_OPT_IMMEDIATE}, {"suspend", required_argument, 0, "suspend an instance", uwsgi_opt_pidfile_signal, (void *) SIGTSTP, UWSGI_OPT_IMMEDIATE}, {"resume", required_argument, 0, "resume an instance", uwsgi_opt_pidfile_signal, (void *) SIGTSTP, UWSGI_OPT_IMMEDIATE}, + + {"connect-and-read", required_argument, 0, "connect to a scoekt and wait for data from it", uwsgi_opt_connect_and_read, NULL, UWSGI_OPT_IMMEDIATE}, + {"listen", required_argument, 'l', "set the socket listen queue size", uwsgi_opt_set_int, &uwsgi.listen_queue, 0}, {"max-vars", required_argument, 'v', "set the amount of internal iovec/vars structures", uwsgi_opt_max_vars, NULL, 0}, {"buffer-size", required_argument, 'b', "set internal buffer size", uwsgi_opt_set_int, &uwsgi.buffer_size, 0}, @@ -4404,3 +4407,21 @@ void uwsgi_opt_cflags(char *opt, char *filename, void *foobar) { fprintf(stdout, "%.*s\n", (int) len/2, base); exit(0); } + +void uwsgi_opt_connect_and_read(char *opt, char *address, void *foobar) { + + char buf[8192]; + + int fd = uwsgi_connect(address, -1, 0); + for(;;) { + int ret = uwsgi_waitfd(fd, -1); + if (ret <= 0) { + exit(0); + } + ssize_t len = read(fd, buf, 8192); + if (len <= 0) { + exit(0); + } + uwsgi_log("%.*s", (int) len, buf); + } +} diff --git a/uwsgi.h b/uwsgi.h index 8a3466fb..3c84ab07 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -2899,6 +2899,7 @@ char *uwsgi_substitute(char *, char *, char *); void manage_cluster_message(char *, int); void uwsgi_opt_add_custom_option(char *, char *, void *); void uwsgi_opt_cflags(char *, char *, void *); +void uwsgi_opt_connect_and_read(char *, char *, void *); struct uwsgi_string_list *uwsgi_string_list_has_item(struct uwsgi_string_list *, char *, size_t);