/* ------------------------------------------------------------------------- */ /* * Copyright 2007-2023 GRAHAM DUMPLETON * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* ------------------------------------------------------------------------- */ #include "wsgi_metrics.h" #include "wsgi_apache.h" #include "wsgi_daemon.h" #include "wsgi_server.h" #include "wsgi_memory.h" #include "wsgi_logger.h" #include "wsgi_thread.h" /* ------------------------------------------------------------------------- */ /* * Thread utilisation. On start and end of requests, * and when utilisation is requested, we acrue an * ongoing utilisation time value so can monitor how * busy we are handling requests. */ apr_uint64_t wsgi_total_requests = 0; int wsgi_active_requests = 0; static double wsgi_thread_utilization = 0.0; static apr_time_t wsgi_utilization_last = 0; /* Request tracking and timing. */ apr_thread_mutex_t* wsgi_monitor_lock = NULL; static double wsgi_utilization_time(int adjustment, apr_uint64_t* request_count) { apr_time_t now; double utilization = wsgi_thread_utilization; apr_thread_mutex_lock(wsgi_monitor_lock); now = apr_time_now(); if (wsgi_utilization_last != 0.0) { utilization = (now - wsgi_utilization_last) / 1000000.0; if (utilization < 0) utilization = 0; utilization = wsgi_active_requests * utilization; wsgi_thread_utilization += utilization; utilization = wsgi_thread_utilization; } wsgi_utilization_last = now; wsgi_active_requests += adjustment; if (adjustment < 0) wsgi_total_requests += -adjustment; if (request_count) *request_count = wsgi_total_requests; apr_thread_mutex_unlock(wsgi_monitor_lock); return utilization; } static int wsgi_request_metrics_enabled = 0; static apr_uint64_t wsgi_sample_requests = 0; static double wsgi_server_time_total = 0; static int wsgi_server_time_buckets[16]; static double wsgi_queue_time_total = 0; static int wsgi_queue_time_buckets[16]; static double wsgi_daemon_time_total = 0; static int wsgi_daemon_time_buckets[16]; static double wsgi_application_time_total = 0; static int wsgi_application_time_buckets[16]; static int* wsgi_request_threads_buckets = NULL; void wsgi_record_time_in_buckets(int* buckets, double duration) { int index = 0; double threshold = 0.005; while (index < 14) { if (duration <= threshold) { buckets[index] += 1; return; } threshold *= 2; index += 1; } buckets[index] += 1; } void wsgi_record_request_times(apr_time_t request_start, apr_time_t queue_start, apr_time_t daemon_start, apr_time_t application_start, apr_time_t application_finish) { double server_time = 0.0; double queue_time = 0.0; double daemon_time = 0.0; double application_time = 0.0; if (wsgi_request_metrics_enabled == 0) return; if (queue_start) { server_time = apr_time_sec((double)(queue_start-request_start)); queue_time = apr_time_sec((double)(daemon_start-queue_start)); daemon_time = apr_time_sec((double)(application_start-daemon_start)); } else { server_time = apr_time_sec((double)(application_start-request_start)); daemon_time = 0; queue_time = 0; } application_time = (apr_time_sec((double)(application_finish- application_start))); apr_thread_mutex_lock(wsgi_monitor_lock); wsgi_sample_requests += 1; wsgi_server_time_total += server_time; wsgi_queue_time_total += queue_time; wsgi_daemon_time_total += daemon_time; wsgi_application_time_total += application_time; wsgi_record_time_in_buckets(&wsgi_server_time_buckets[0], server_time); #if defined(MOD_WSGI_WITH_DAEMONS) if (wsgi_daemon_process) { wsgi_record_time_in_buckets(&wsgi_queue_time_buckets[0], queue_time); wsgi_record_time_in_buckets(&wsgi_daemon_time_buckets[0], daemon_time); } #endif wsgi_record_time_in_buckets(&wsgi_application_time_buckets[0], application_time); apr_thread_mutex_unlock(wsgi_monitor_lock); } WSGIThreadInfo *wsgi_start_request(request_rec *r) { WSGIThreadInfo *thread_info; PyObject *module = NULL; thread_info = wsgi_thread_info(1, 1); thread_info->request_data = PyDict_New(); #if AP_MODULE_MAGIC_AT_LEAST(20100923,2) #if PY_MAJOR_VERSION >= 3 thread_info->request_id = PyUnicode_DecodeLatin1(r->log_id, strlen(r->log_id), NULL); #else thread_info->request_id = PyString_FromString(r->log_id); #endif module = PyImport_ImportModule("mod_wsgi"); if (module) { PyObject *dict = NULL; PyObject *requests = NULL; dict = PyModule_GetDict(module); requests = PyDict_GetItemString(dict, "active_requests"); if (requests) PyDict_SetItem(requests, thread_info->request_id, thread_info->request_data); Py_DECREF(module); } else PyErr_Clear(); #endif wsgi_utilization_time(1, NULL); return thread_info; } void wsgi_end_request(void) { WSGIThreadInfo *thread_info; PyObject *module = NULL; thread_info = wsgi_thread_info(0, 1); if (thread_info) { if (wsgi_request_threads_buckets) wsgi_request_threads_buckets[thread_info->thread_id-1] += 1; #if AP_MODULE_MAGIC_AT_LEAST(20100923,2) module = PyImport_ImportModule("mod_wsgi"); if (module) { PyObject *dict = NULL; PyObject *requests = NULL; dict = PyModule_GetDict(module); requests = PyDict_GetItemString(dict, "active_requests"); PyDict_DelItem(requests, thread_info->request_id); Py_DECREF(module); } else PyErr_Clear(); #endif if (thread_info->log_buffer) Py_CLEAR(thread_info->log_buffer); if (thread_info->request_id) Py_CLEAR(thread_info->request_id); if (thread_info->request_data) Py_CLEAR(thread_info->request_data); } wsgi_utilization_time(-1, NULL); } /* ------------------------------------------------------------------------- */ static int wsgi_interns_initialized = 0; WSGI_STATIC_INTERNED_STRING(server_limit); WSGI_STATIC_INTERNED_STRING(thread_limit); WSGI_STATIC_INTERNED_STRING(running_generation); WSGI_STATIC_INTERNED_STRING(restart_time); WSGI_STATIC_INTERNED_STRING(current_time); WSGI_STATIC_INTERNED_STRING(running_time); WSGI_STATIC_INTERNED_STRING(process_num); WSGI_STATIC_INTERNED_STRING(pid); WSGI_STATIC_INTERNED_STRING(generation); WSGI_STATIC_INTERNED_STRING(quiescing); WSGI_STATIC_INTERNED_STRING(workers); WSGI_STATIC_INTERNED_STRING(thread_num); WSGI_STATIC_INTERNED_STRING(status); WSGI_STATIC_INTERNED_STRING(access_count); WSGI_STATIC_INTERNED_STRING(bytes_served); WSGI_STATIC_INTERNED_STRING(start_time); WSGI_STATIC_INTERNED_STRING(stop_time); WSGI_STATIC_INTERNED_STRING(last_used); WSGI_STATIC_INTERNED_STRING(client); WSGI_STATIC_INTERNED_STRING(request); WSGI_STATIC_INTERNED_STRING(vhost); WSGI_STATIC_INTERNED_STRING(processes); WSGI_STATIC_INTERNED_STRING(request_count); WSGI_STATIC_INTERNED_STRING(request_busy_time); WSGI_STATIC_INTERNED_STRING(memory_max_rss); WSGI_STATIC_INTERNED_STRING(memory_rss); WSGI_STATIC_INTERNED_STRING(cpu_user_time); WSGI_STATIC_INTERNED_STRING(cpu_system_time); WSGI_STATIC_INTERNED_STRING(request_threads); WSGI_STATIC_INTERNED_STRING(active_requests); WSGI_STATIC_INTERNED_STRING(threads); WSGI_STATIC_INTERNED_STRING(thread_id); WSGI_STATIC_INTERNED_STRING(sample_period); WSGI_STATIC_INTERNED_STRING(request_threads_maximum); WSGI_STATIC_INTERNED_STRING(request_threads_started); WSGI_STATIC_INTERNED_STRING(request_threads_active); WSGI_STATIC_INTERNED_STRING(capacity_utilization); WSGI_STATIC_INTERNED_STRING(request_throughput); WSGI_STATIC_INTERNED_STRING(server_time); WSGI_STATIC_INTERNED_STRING(queue_time); WSGI_STATIC_INTERNED_STRING(daemon_time); WSGI_STATIC_INTERNED_STRING(application_time); WSGI_STATIC_INTERNED_STRING(server_time_buckets); WSGI_STATIC_INTERNED_STRING(queue_time_buckets); WSGI_STATIC_INTERNED_STRING(daemon_time_buckets); WSGI_STATIC_INTERNED_STRING(application_time_buckets); WSGI_STATIC_INTERNED_STRING(request_threads_buckets); static PyObject *wsgi_status_flags[SERVER_NUM_STATUS]; #define WSGI_CREATE_STATUS_FLAG(name, val) \ wsgi_status_flags[name] = wsgi_PyString_InternFromString(val) static void wsgi_initialize_interned_strings(void) { /* Initialise interned strings the first time. */ if (!wsgi_interns_initialized) { WSGI_CREATE_INTERNED_STRING_ID(server_limit); WSGI_CREATE_INTERNED_STRING_ID(thread_limit); WSGI_CREATE_INTERNED_STRING_ID(running_generation); WSGI_CREATE_INTERNED_STRING_ID(restart_time); WSGI_CREATE_INTERNED_STRING_ID(current_time); WSGI_CREATE_INTERNED_STRING_ID(running_time); WSGI_CREATE_INTERNED_STRING_ID(process_num); WSGI_CREATE_INTERNED_STRING_ID(pid); WSGI_CREATE_INTERNED_STRING_ID(generation); WSGI_CREATE_INTERNED_STRING_ID(quiescing); WSGI_CREATE_INTERNED_STRING_ID(workers); WSGI_CREATE_INTERNED_STRING_ID(thread_num); WSGI_CREATE_INTERNED_STRING_ID(status); WSGI_CREATE_INTERNED_STRING_ID(access_count); WSGI_CREATE_INTERNED_STRING_ID(bytes_served); WSGI_CREATE_INTERNED_STRING_ID(start_time); WSGI_CREATE_INTERNED_STRING_ID(stop_time); WSGI_CREATE_INTERNED_STRING_ID(last_used); WSGI_CREATE_INTERNED_STRING_ID(client); WSGI_CREATE_INTERNED_STRING_ID(request); WSGI_CREATE_INTERNED_STRING_ID(vhost); WSGI_CREATE_INTERNED_STRING_ID(processes); WSGI_CREATE_INTERNED_STRING_ID(request_count); WSGI_CREATE_INTERNED_STRING_ID(request_busy_time); WSGI_CREATE_INTERNED_STRING_ID(memory_max_rss); WSGI_CREATE_INTERNED_STRING_ID(memory_rss); WSGI_CREATE_INTERNED_STRING_ID(cpu_user_time); WSGI_CREATE_INTERNED_STRING_ID(cpu_system_time); WSGI_CREATE_INTERNED_STRING_ID(request_threads); WSGI_CREATE_INTERNED_STRING_ID(active_requests); WSGI_CREATE_INTERNED_STRING_ID(threads); WSGI_CREATE_INTERNED_STRING_ID(thread_id); WSGI_CREATE_INTERNED_STRING_ID(sample_period); WSGI_CREATE_INTERNED_STRING_ID(request_threads_maximum); WSGI_CREATE_INTERNED_STRING_ID(request_threads_started); WSGI_CREATE_INTERNED_STRING_ID(request_threads_active); WSGI_CREATE_INTERNED_STRING_ID(capacity_utilization); WSGI_CREATE_INTERNED_STRING_ID(request_throughput); WSGI_CREATE_INTERNED_STRING_ID(server_time); WSGI_CREATE_INTERNED_STRING_ID(queue_time); WSGI_CREATE_INTERNED_STRING_ID(daemon_time); WSGI_CREATE_INTERNED_STRING_ID(application_time); WSGI_CREATE_INTERNED_STRING_ID(server_time_buckets); WSGI_CREATE_INTERNED_STRING_ID(daemon_time_buckets); WSGI_CREATE_INTERNED_STRING_ID(queue_time_buckets); WSGI_CREATE_INTERNED_STRING_ID(application_time_buckets); WSGI_CREATE_INTERNED_STRING_ID(request_threads_buckets); WSGI_CREATE_STATUS_FLAG(SERVER_DEAD, "."); WSGI_CREATE_STATUS_FLAG(SERVER_READY, "_"); WSGI_CREATE_STATUS_FLAG(SERVER_STARTING, "S"); WSGI_CREATE_STATUS_FLAG(SERVER_BUSY_READ, "R"); WSGI_CREATE_STATUS_FLAG(SERVER_BUSY_WRITE, "W"); WSGI_CREATE_STATUS_FLAG(SERVER_BUSY_KEEPALIVE, "K"); WSGI_CREATE_STATUS_FLAG(SERVER_BUSY_LOG, "L"); WSGI_CREATE_STATUS_FLAG(SERVER_BUSY_DNS, "D"); WSGI_CREATE_STATUS_FLAG(SERVER_CLOSING, "C"); WSGI_CREATE_STATUS_FLAG(SERVER_GRACEFUL, "G"); WSGI_CREATE_STATUS_FLAG(SERVER_IDLE_KILL, "I"); wsgi_interns_initialized = 1; } } /* ------------------------------------------------------------------------- */ static PyObject *wsgi_request_metrics(void) { PyObject *result = NULL; PyObject *object = NULL; apr_time_t stop_time; double stop_request_busy_time = 0.0; apr_uint64_t stop_request_count = 0.0; double request_busy_time = 0.0; double capacity_utilization = 0.0; static double start_time = 0.0; static double start_cpu_system_time = 0.0; static double start_cpu_user_time = 0.0; static double start_request_busy_time = 0.0; static apr_uint64_t start_request_count = 0; double sample_period = 0.0; apr_uint64_t request_count = 0; double request_throughput = 0.0; double stop_cpu_system_time = 0.0; double stop_cpu_user_time = 0.0; double cpu_system_time = 0.0; double cpu_user_time = 0.0; double total_cpu_time = 0.0; static int request_threads_maximum = 0; apr_uint64_t interval_requests = 0; double server_time_total = 0; double server_time_avg = 0; double queue_time_total = 0; double queue_time_avg = 0; double daemon_time_total = 0; double daemon_time_avg = 0; double application_time_total = 0; double application_time_avg = 0; int request_threads_active = 0; int i; #ifdef HAVE_TIMES struct tms tmsbuf; static float tick = 0.0; if (!tick) { #ifdef _SC_CLK_TCK tick = sysconf(_SC_CLK_TCK); #else tick = HZ; #endif } #endif if (!wsgi_interns_initialized) wsgi_initialize_interned_strings(); if (!request_threads_maximum) { int is_threaded = 0; #if defined(MOD_WSGI_WITH_DAEMONS) if (wsgi_daemon_process) { request_threads_maximum = wsgi_daemon_process->group->threads; } else { ap_mpm_query(AP_MPMQ_IS_THREADED, &is_threaded); if (is_threaded != AP_MPMQ_NOT_SUPPORTED) { ap_mpm_query(AP_MPMQ_MAX_THREADS, &request_threads_maximum); } } #else ap_mpm_query(AP_MPMQ_IS_THREADED, &is_threaded); if (is_threaded != AP_MPMQ_NOT_SUPPORTED) { ap_mpm_query(AP_MPMQ_MAX_THREADS, &request_threads_maximum); } #endif request_threads_maximum = ((request_threads_maximum <= 0) ? 1 : request_threads_maximum); wsgi_request_threads_buckets = (int *)apr_pcalloc( wsgi_server_config->pool, request_threads_maximum*sizeof( wsgi_request_threads_buckets[0])); } result = PyDict_New(); stop_time = apr_time_now(); stop_request_busy_time = wsgi_utilization_time(0, &stop_request_count); if (!start_time) { start_time = stop_time; start_request_busy_time = stop_request_busy_time; start_request_count = stop_request_count; #ifdef HAVE_TIMES times(&tmsbuf); start_cpu_user_time = tmsbuf.tms_utime / tick; start_cpu_system_time = tmsbuf.tms_stime / tick; #else start_cpu_user_time = 0.0; start_cpu_system_time = 0.0; #endif apr_thread_mutex_lock(wsgi_monitor_lock); wsgi_sample_requests = 0; wsgi_server_time_total = 0.0; wsgi_queue_time_total = 0.0; wsgi_daemon_time_total = 0.0; wsgi_application_time_total = 0.0; wsgi_request_metrics_enabled = 1; apr_thread_mutex_unlock(wsgi_monitor_lock); return result; } object = wsgi_PyInt_FromLong(getpid()); PyDict_SetItem(result, WSGI_INTERNED_STRING(pid), object); Py_DECREF(object); object = PyFloat_FromDouble(apr_time_sec((double)start_time)); PyDict_SetItem(result, WSGI_INTERNED_STRING(start_time), object); Py_DECREF(object); object = PyFloat_FromDouble(apr_time_sec((double)stop_time)); PyDict_SetItem(result, WSGI_INTERNED_STRING(stop_time), object); Py_DECREF(object); sample_period = (apr_time_sec((double)stop_time) - apr_time_sec((double)start_time)); object = PyFloat_FromDouble(sample_period); PyDict_SetItem(result, WSGI_INTERNED_STRING(sample_period), object); Py_DECREF(object); #ifdef HAVE_TIMES times(&tmsbuf); stop_cpu_user_time = tmsbuf.tms_utime / tick; stop_cpu_system_time = tmsbuf.tms_stime / tick; cpu_user_time = ((stop_cpu_user_time - start_cpu_user_time) / sample_period); cpu_system_time = ((stop_cpu_system_time - start_cpu_system_time) / sample_period); total_cpu_time += cpu_user_time; total_cpu_time += cpu_system_time; object = PyFloat_FromDouble(cpu_user_time); PyDict_SetItem(result, WSGI_INTERNED_STRING(cpu_user_time), object); Py_DECREF(object); object = PyFloat_FromDouble(cpu_system_time); PyDict_SetItem(result, WSGI_INTERNED_STRING(cpu_system_time), object); Py_DECREF(object); #else object = PyFloat_FromDouble(0.0); PyDict_SetItem(result, WSGI_INTERNED_STRING(cpu_user_time), object); Py_DECREF(object); object = PyFloat_FromDouble(0.0); PyDict_SetItem(result, WSGI_INTERNED_STRING(cpu_system_time), object); Py_DECREF(object); #endif object = wsgi_PyInt_FromLongLong(wsgi_get_peak_memory_RSS()); PyDict_SetItem(result, WSGI_INTERNED_STRING(memory_max_rss), object); Py_DECREF(object); object = wsgi_PyInt_FromLongLong(wsgi_get_current_memory_RSS()); PyDict_SetItem(result, WSGI_INTERNED_STRING(memory_rss), object); Py_DECREF(object); object = wsgi_PyInt_FromLong(request_threads_maximum); PyDict_SetItem(result, WSGI_INTERNED_STRING(request_threads_maximum), object); Py_DECREF(object); object = wsgi_PyInt_FromLong(wsgi_request_threads); PyDict_SetItem(result, WSGI_INTERNED_STRING(request_threads_started), object); Py_DECREF(object); request_busy_time = stop_request_busy_time - start_request_busy_time; capacity_utilization = (request_busy_time / sample_period / request_threads_maximum); object = PyFloat_FromDouble(capacity_utilization); PyDict_SetItem(result, WSGI_INTERNED_STRING(capacity_utilization), object); Py_DECREF(object); request_count = stop_request_count - start_request_count; object = wsgi_PyInt_FromLongLong(request_count); PyDict_SetItem(result, WSGI_INTERNED_STRING(request_count), object); Py_DECREF(object); request_throughput = sample_period ? request_count / sample_period : 0; object = PyFloat_FromDouble(request_throughput); PyDict_SetItem(result, WSGI_INTERNED_STRING(request_throughput), object); Py_DECREF(object); start_time = stop_time; start_request_busy_time = stop_request_busy_time; start_request_count = stop_request_count; start_cpu_user_time = stop_cpu_user_time; start_cpu_system_time = stop_cpu_system_time; apr_thread_mutex_lock(wsgi_monitor_lock); interval_requests = wsgi_sample_requests; server_time_total = wsgi_server_time_total; queue_time_total = wsgi_queue_time_total; daemon_time_total = wsgi_daemon_time_total; application_time_total = wsgi_application_time_total; object = PyList_New(16); for (i=0; i<16; i++) { PyList_SET_ITEM(object, i, wsgi_PyInt_FromLong( wsgi_server_time_buckets[i])); } PyDict_SetItem(result, WSGI_INTERNED_STRING(server_time_buckets), object); Py_DECREF(object); object = PyList_New(16); for (i=0; i<16; i++) { PyList_SET_ITEM(object, i, wsgi_PyInt_FromLong( wsgi_queue_time_buckets[i])); } PyDict_SetItem(result, WSGI_INTERNED_STRING(queue_time_buckets), object); Py_DECREF(object); object = PyList_New(16); for (i=0; i<16; i++) { PyList_SET_ITEM(object, i, wsgi_PyInt_FromLong( wsgi_daemon_time_buckets[i])); } PyDict_SetItem(result, WSGI_INTERNED_STRING(daemon_time_buckets), object); Py_DECREF(object); object = PyList_New(16); for (i=0; i<16; i++) { PyList_SET_ITEM(object, i, wsgi_PyInt_FromLong( wsgi_application_time_buckets[i])); } PyDict_SetItem(result, WSGI_INTERNED_STRING(application_time_buckets), object); Py_DECREF(object); object = PyList_New(request_threads_maximum); for (i=0; ielts; for (i=0; inelts; i++) { PyObject *entry = NULL; if (thread_info[i]->request_thread) { entry = PyDict_New(); object = wsgi_PyInt_FromLong(thread_info[i]->thread_id); PyDict_SetItem(entry, WSGI_INTERNED_STRING(thread_id), object); Py_DECREF(object); object = wsgi_PyInt_FromLongLong(thread_info[i]->request_count); PyDict_SetItem(entry, WSGI_INTERNED_STRING(request_count), object); Py_DECREF(object); PyList_Append(thread_list, entry); Py_DECREF(entry); } } Py_DECREF(thread_list); return result; } PyMethodDef wsgi_process_metrics_method[] = { { "process_metrics", (PyCFunction)wsgi_process_metrics, METH_NOARGS, 0 }, { NULL }, }; /* ------------------------------------------------------------------------- */ static PyObject *wsgi_server_metrics(void) { PyObject *scoreboard_dict = NULL; PyObject *process_list = NULL; PyObject *object = NULL; apr_time_t current_time; apr_interval_time_t running_time; global_score *gs_record; worker_score *ws_record; process_score *ps_record; int j, i; if (!wsgi_interns_initialized) wsgi_initialize_interned_strings(); /* Scoreboard needs to exist and server metrics enabled. */ if (!ap_exists_scoreboard_image()) { Py_INCREF(Py_None); return Py_None; } if (!wsgi_daemon_pool) { if (!wsgi_server_config->server_metrics) { Py_INCREF(Py_None); return Py_None; } } #if defined(MOD_WSGI_WITH_DAEMONS) else { if (!wsgi_daemon_process->group->server_metrics) { Py_INCREF(Py_None); return Py_None; } } #endif gs_record = ap_get_scoreboard_global(); if (!gs_record) { Py_INCREF(Py_None); return Py_None; } /* Return everything in a dictionary. Start with global. */ scoreboard_dict = PyDict_New(); object = wsgi_PyInt_FromLong(gs_record->server_limit); PyDict_SetItem(scoreboard_dict, WSGI_INTERNED_STRING(server_limit), object); Py_DECREF(object); object = wsgi_PyInt_FromLong(gs_record->thread_limit); PyDict_SetItem(scoreboard_dict, WSGI_INTERNED_STRING(thread_limit), object); Py_DECREF(object); object = wsgi_PyInt_FromLong(gs_record->running_generation); PyDict_SetItem(scoreboard_dict, WSGI_INTERNED_STRING(running_generation), object); Py_DECREF(object); object = PyFloat_FromDouble(apr_time_sec(( double)gs_record->restart_time)); PyDict_SetItem(scoreboard_dict, WSGI_INTERNED_STRING(restart_time), object); Py_DECREF(object); current_time = apr_time_now(); object = PyFloat_FromDouble(apr_time_sec((double)current_time)); PyDict_SetItem(scoreboard_dict, WSGI_INTERNED_STRING(current_time), object); Py_DECREF(object); running_time = (apr_uint32_t)apr_time_sec((double) current_time - ap_scoreboard_image->global->restart_time); object = wsgi_PyInt_FromLongLong(running_time); PyDict_SetItem(scoreboard_dict, WSGI_INTERNED_STRING(running_time), object); Py_DECREF(object); /* Now add in the processes/workers. */ process_list = PyList_New(0); for (i = 0; i < gs_record->server_limit; ++i) { PyObject *process_dict = NULL; PyObject *worker_list = NULL; ps_record = ap_get_scoreboard_process(i); process_dict = PyDict_New(); PyList_Append(process_list, process_dict); object = wsgi_PyInt_FromLong(i); PyDict_SetItem(process_dict, WSGI_INTERNED_STRING(process_num), object); Py_DECREF(object); object = wsgi_PyInt_FromLong(ps_record->pid); PyDict_SetItem(process_dict, WSGI_INTERNED_STRING(pid), object); Py_DECREF(object); object = wsgi_PyInt_FromLong(ps_record->generation); PyDict_SetItem(process_dict, WSGI_INTERNED_STRING(generation), object); Py_DECREF(object); object = PyBool_FromLong(ps_record->quiescing); PyDict_SetItem(process_dict, WSGI_INTERNED_STRING(quiescing), object); Py_DECREF(object); worker_list = PyList_New(0); PyDict_SetItem(process_dict, WSGI_INTERNED_STRING(workers), worker_list); for (j = 0; j < gs_record->thread_limit; ++j) { PyObject *worker_dict = NULL; #if AP_MODULE_MAGIC_AT_LEAST(20071023,0) ws_record = ap_get_scoreboard_worker_from_indexes(i, j); #else ws_record = ap_get_scoreboard_worker(i, j); #endif worker_dict = PyDict_New(); PyList_Append(worker_list, worker_dict); object = wsgi_PyInt_FromLong(ws_record->thread_num); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(thread_num), object); Py_DECREF(object); object = wsgi_PyInt_FromLong(ws_record->generation); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(generation), object); Py_DECREF(object); object = wsgi_status_flags[ws_record->status]; PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(status), object); object = wsgi_PyInt_FromLong(ws_record->access_count); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(access_count), object); Py_DECREF(object); object = wsgi_PyInt_FromUnsignedLongLong(ws_record->bytes_served); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(bytes_served), object); Py_DECREF(object); object = PyFloat_FromDouble(apr_time_sec( (double)ws_record->start_time)); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(start_time), object); Py_DECREF(object); object = PyFloat_FromDouble(apr_time_sec( (double)ws_record->stop_time)); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(stop_time), object); Py_DECREF(object); object = PyFloat_FromDouble(apr_time_sec( (double)ws_record->last_used)); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(last_used), object); Py_DECREF(object); object = wsgi_PyString_FromString(ws_record->client); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(client), object); Py_DECREF(object); object = wsgi_PyString_FromString(ws_record->request); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(request), object); Py_DECREF(object); object = wsgi_PyString_FromString(ws_record->vhost); PyDict_SetItem(worker_dict, WSGI_INTERNED_STRING(vhost), object); Py_DECREF(object); Py_DECREF(worker_dict); } Py_DECREF(worker_list); Py_DECREF(process_dict); } PyDict_SetItem(scoreboard_dict, WSGI_INTERNED_STRING(processes), process_list); Py_DECREF(process_list); return scoreboard_dict; } /* ------------------------------------------------------------------------- */ PyMethodDef wsgi_server_metrics_method[] = { { "server_metrics", (PyCFunction)wsgi_server_metrics, METH_NOARGS, 0 }, { NULL }, }; /* ------------------------------------------------------------------------- */ static PyObject *wsgi_subscribe_events(PyObject *self, PyObject *args) { PyObject *callback = NULL; PyObject *module = NULL; if (!PyArg_ParseTuple(args, "O", &callback)) return NULL; module = PyImport_ImportModule("mod_wsgi"); if (module) { PyObject *dict = NULL; PyObject *list = NULL; dict = PyModule_GetDict(module); list = PyDict_GetItemString(dict, "event_callbacks"); if (list) PyList_Append(list, callback); else return NULL; Py_DECREF(module); } else return NULL; Py_INCREF(Py_None); return Py_None; } static PyObject *wsgi_subscribe_shutdown(PyObject *self, PyObject *args) { PyObject *callback = NULL; PyObject *module = NULL; if (!PyArg_ParseTuple(args, "O", &callback)) return NULL; module = PyImport_ImportModule("mod_wsgi"); if (module) { PyObject *dict = NULL; PyObject *list = NULL; dict = PyModule_GetDict(module); list = PyDict_GetItemString(dict, "shutdown_callbacks"); if (list) PyList_Append(list, callback); else return NULL; Py_DECREF(module); } else return NULL; Py_INCREF(Py_None); return Py_None; } long wsgi_event_subscribers(void) { PyObject *module = NULL; module = PyImport_ImportModule("mod_wsgi"); if (module) { PyObject *dict = NULL; PyObject *list = NULL; long result = 0; dict = PyModule_GetDict(module); list = PyDict_GetItemString(dict, "event_callbacks"); if (list) result = PyList_Size(list); Py_DECREF(module); return result; } else return 0; } void wsgi_call_callbacks(const char *name, PyObject *callbacks, PyObject *event) { int i; for (i=0; irequest_data) { PyErr_SetString(PyExc_RuntimeError, "no active request for thread"); return NULL; } Py_INCREF(thread_info->request_data); return thread_info->request_data; } /* ------------------------------------------------------------------------- */ PyMethodDef wsgi_request_data_method[] = { { "request_data", (PyCFunction)wsgi_request_data, METH_NOARGS, 0 }, { NULL }, }; /* ------------------------------------------------------------------------- */ /* vi: set sw=4 expandtab : */