mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-09-05 13:11:33 +00:00
completed the python tracebacker
This commit is contained in:
@@ -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) {
|
||||
|
||||
|
||||
@@ -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 = "<UnnamedPythonThread>";
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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) ]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user