Files
uwsgi/master_utils.c
T
2012-03-16 06:52:48 +01:00

831 lines
22 KiB
C

#include "uwsgi.h"
extern struct uwsgi_server uwsgi;
void worker_wakeup() {
}
int uwsgi_calc_cheaper(void) {
int i;
int needed_workers = uwsgi.cheaper_algo();
if (needed_workers > 0) {
for (i = 1; i <= uwsgi.numproc; i++) {
if (uwsgi.workers[i].cheaped == 1 && uwsgi.workers[i].pid == 0) {
if (uwsgi_respawn_worker(i))
return 0;
needed_workers--;
}
if (needed_workers == 0)
break;
}
}
else if (needed_workers < 0) {
int oldest_worker = 0;
time_t oldest_worker_spawn = INT_MAX;
for (i = 1; i <= uwsgi.numproc; i++) {
if (uwsgi.workers[i].cheaped == 0 && uwsgi.workers[i].pid > 0) {
if (uwsgi.workers[i].last_spawn < oldest_worker_spawn) {
oldest_worker_spawn = uwsgi.workers[i].last_spawn;
oldest_worker = i;
}
}
}
if (oldest_worker > 0) {
#ifdef UWSGI_DEBUG
uwsgi_log("worker %d should die...\n", oldest_worker);
#endif
uwsgi.workers[oldest_worker].cheaped = 1;
uwsgi.workers[oldest_worker].manage_next_request = 0;
// wakeup task in case of wait
(void) kill(uwsgi.workers[oldest_worker].pid, SIGWINCH);
}
}
return 1;
}
/*
-- Cheaper, spare algorithm, adapted from old-fashioned spare system --
when all of the workers are busy, the overload_count is incremented.
as soon as overload_count is higher than uwsgi.cheaper_overload (--cheaper-overload options)
at most cheaper_step (default to 1) new workers are spawned.
when at least one worker is free, the overload_count is decremented and the idle_count is incremented.
If overload_count reaches 0, the system will count active workers (the ones uncheaped) and busy workers (the ones running a request)
if there is exacly 1 free worker we are in "stable state" (1 spare worker available). no worker will be touched.
if the number of active workers is higher than uwsgi.cheaper_count and at least uwsgi.cheaper_overload cycles are passed from the last
"cheap it" procedure, then cheap a worker.
Example:
10 processes
2 cheaper
2 cheaper step
3 cheaper_overload
1 second master cycle
there are 7 workers running (triggered by some kind of spike activity).
Of this, 6 are busy, 1 is free. We are in stable state.
After a bit the spike disappear and idle_count start to increase.
After 3 seconds (uwsgi.cheaper_overload cycles) the oldest worker will be cheaped. This will happens
every seconds (uwsgi.cheaper_overload cycles) til the number of workers is == uwsgi.cheaper_count.
If during the "cheap them all" procedure, an overload condition come again (another spike) the "cheap them all"
will be interrupted.
*/
int uwsgi_cheaper_algo_spare(void) {
int i;
static uint64_t overload_count = 0;
static uint64_t idle_count = 0;
// step 1 -> count the number of busy workers
for (i = 1; i <= uwsgi.numproc; i++) {
if (uwsgi.workers[i].cheaped == 0 && uwsgi.workers[i].pid > 0) {
// if a non-busy worker is found, the overload_count is decremented and stop the cycle
if (uwsgi.workers[i].busy == 0) {
if (overload_count > 0)
overload_count--;
goto healthy;
}
}
}
overload_count++;
idle_count = 0;
healthy:
// are we overloaded ?
if (overload_count > uwsgi.cheaper_overload) {
#ifdef UWSGI_DEBUG
uwsgi_log("overloaded !!!\n");
#endif
// activate the first available worker (taking step into account)
int decheaped = 0;
// search for cheaped workers
for (i = 1; i <= uwsgi.numproc; i++) {
if (uwsgi.workers[i].cheaped == 1 && uwsgi.workers[i].pid == 0) {
decheaped++;
if (decheaped >= uwsgi.cheaper_step)
break;
}
}
// reset overload
overload_count = 0;
// return the maximum number of workers to spawn
return decheaped;
}
// we are no more overloaded
else if (overload_count == 0) {
// how many active workers ?
int active_workers = 0;
int busy_workers = 0;
for (i = 1; i <= uwsgi.numproc; i++) {
if (uwsgi.workers[i].cheaped == 0 && uwsgi.workers[i].pid > 0) {
active_workers++;
if (uwsgi.workers[i].busy == 1)
busy_workers++;
}
}
#ifdef UWSGI_DEBUG
uwsgi_log("active workers %d busy_workers %d\n", active_workers, busy_workers);
#endif
// special condition: uwsgi.cheaper running workers and 1 free
if (active_workers > busy_workers && active_workers - busy_workers == 1) {
#ifdef UWSGI_DEBUG
uwsgi_log("stable status: 1 spare worker\n");
#endif
return 0;
}
idle_count++;
if (active_workers > uwsgi.cheaper_count && idle_count % uwsgi.cheaper_overload == 0) {
// we are in "cheap them all"
return -1;
}
}
return 0;
}
/*
-- Cheaper, backlog algorithm (supported only on Linux) --
increse the number of workers when the listen queue is higher than uwsgi.cheaper_overload.
Decrese when lower.
*/
int uwsgi_cheaper_algo_backlog(void) {
int i;
#ifdef __linux__
int backlog = uwsgi.shared->options[UWSGI_OPTION_BACKLOG_STATUS];
#else
int backlog = 0;
#endif
if (backlog > (int)uwsgi.cheaper_overload) {
// activate the first available worker (taking step into account)
int decheaped = 0;
// search for cheaped workers
for (i = 1; i <= uwsgi.numproc; i++) {
if (uwsgi.workers[i].cheaped == 1 && uwsgi.workers[i].pid == 0) {
decheaped++;
if (decheaped >= uwsgi.cheaper_step)
break;
}
}
// return the maximum number of workers to spawn
return decheaped;
}
else if (backlog < (int) uwsgi.cheaper_overload) {
int active_workers = 0;
for (i = 1; i <= uwsgi.numproc; i++) {
if (uwsgi.workers[i].cheaped == 0 && uwsgi.workers[i].pid > 0) {
active_workers++;
}
}
if (active_workers > uwsgi.cheaper_count) {
return -1;
}
}
return 0;
}
// reload uWSGI, close unneded file descriptor, restore the original environment and re-exec the binary
void uwsgi_reload(char **argv) {
int i;
int waitpid_status;
// call a series of waitpid to ensure all processes (gateways, mules and daemons) are dead
for (i = 0; i < (ushared->gateways_cnt + uwsgi.daemons_cnt + uwsgi.mules_cnt); i++) {
waitpid(WAIT_ANY, &waitpid_status, WNOHANG);
}
if (uwsgi.exit_on_reload) {
uwsgi_log("uWSGI: GAME OVER (insert coin)\n");
exit(0);
}
uwsgi_log("binary reloading uWSGI...\n");
uwsgi_log("chdir() to %s\n", uwsgi.cwd);
if (chdir(uwsgi.cwd)) {
uwsgi_error("chdir()");
}
/* check fd table (a module can obviosly open some fd on initialization...) */
uwsgi_log("closing all non-uwsgi socket fds > 2 (max_fd = %d)...\n", (int)uwsgi.max_fd);
for (i = 3; i < (int)uwsgi.max_fd; i++) {
int found = 0;
struct uwsgi_socket *uwsgi_sock = uwsgi.sockets;
while (uwsgi_sock) {
if (i == uwsgi_sock->fd) {
uwsgi_log("found fd %d mapped to socket %d (%s)\n", i, uwsgi_get_socket_num(uwsgi_sock), uwsgi_sock->name);
found = 1;
break;
}
uwsgi_sock = uwsgi_sock->next;
}
if (!found) {
if (uwsgi.has_emperor) {
if (i == uwsgi.emperor_fd) {
found = 1;
}
}
}
if (uwsgi.log_master) {
if (uwsgi.original_log_fd > -1) {
if (i == uwsgi.original_log_fd) {
found = 1;
}
}
if (uwsgi.shared->worker_log_pipe[0] > -1) {
if (i == uwsgi.shared->worker_log_pipe[0]) {
found = 1;
}
}
if (uwsgi.shared->worker_log_pipe[1] > -1) {
if (i == uwsgi.shared->worker_log_pipe[1]) {
found = 1;
}
}
}
if (!found) {
#ifdef __APPLE__
fcntl(i, F_SETFD, FD_CLOEXEC);
#else
close(i);
#endif
}
}
#ifdef UWSGI_AS_SHARED_LIBRARY
return;
#else
uwsgi_log("running %s\n", uwsgi.binary_path);
uwsgi_flush_logs();
argv[0] = uwsgi.binary_path;
//strcpy (argv[0], uwsgi.binary_path);
if (uwsgi.log_master) {
if (uwsgi.original_log_fd > -1) {
dup2(uwsgi.original_log_fd, 1);
dup2(1, 2);
}
if (uwsgi.shared->worker_log_pipe[0] > -1) {
close(uwsgi.shared->worker_log_pipe[0]);
}
if (uwsgi.shared->worker_log_pipe[1] > -1) {
close(uwsgi.shared->worker_log_pipe[1]);
}
}
execvp(uwsgi.binary_path, argv);
uwsgi_error("execvp()");
// never here
exit(1);
#endif
}
void master_check_cluster_nodes() {
int i;
for (i = 0; i < MAX_CLUSTER_NODES; i++) {
struct uwsgi_cluster_node *ucn = &uwsgi.shared->nodes[i];
if (ucn->name[0] != 0 && ucn->type == CLUSTER_NODE_STATIC && ucn->status == UWSGI_NODE_FAILED) {
// should i retry ?
if (uwsgi.master_cycles % ucn->errors == 0) {
if (!uwsgi_ping_node(i, uwsgi.wsgi_req)) {
ucn->status = UWSGI_NODE_OK;
uwsgi_log("re-enabled cluster node %d/%s\n", i, ucn->name);
}
else {
ucn->errors++;
}
}
}
else if (ucn->name[0] != 0 && ucn->type == CLUSTER_NODE_DYNAMIC) {
// if the last_seen attr is higher than 30 secs ago, mark the node as dead
if ((uwsgi.current_time - ucn->last_seen) > 30) {
uwsgi_log_verbose("no presence announce in the last 30 seconds by node %s, i assume it is dead.\n", ucn->name);
ucn->name[0] = 0;
}
}
}
}
void uwsgi_fixup_fds(int wid, int muleid, struct uwsgi_gateway *ug) {
int i;
// close the cache server
if (uwsgi.cache_server_fd != -1) {
close(uwsgi.cache_server_fd);
}
if (uwsgi.master_process) {
if (uwsgi.master_queue > -1)
close(uwsgi.master_queue);
// close gateways
if (!ug) {
for (i = 0; i < ushared->gateways_cnt; i++) {
close(ushared->gateways[i].internal_subscription_pipe[0]);
close(ushared->gateways[i].internal_subscription_pipe[1]);
}
}
struct uwsgi_gateway_socket *ugs = uwsgi.gateway_sockets;
while (ugs) {
if (ug && !strcmp(ug->name, ugs->owner)) {
ugs = ugs->next;
continue;
}
// do not close shared sockets !!!
if (!ugs->shared) {
close(ugs->fd);
}
ugs = ugs->next;
}
// fix the communication pipe
close(uwsgi.shared->worker_signal_pipe[0]);
for (i = 1; i <= uwsgi.numproc; i++) {
if (uwsgi.workers[i].signal_pipe[0] != -1)
close(uwsgi.workers[i].signal_pipe[0]);
if (i != wid) {
if (uwsgi.workers[i].signal_pipe[1] != -1)
close(uwsgi.workers[i].signal_pipe[1]);
}
}
#ifdef UWSGI_SPOOLER
if (uwsgi.i_am_a_spooler && uwsgi.i_am_a_spooler->pid != getpid()) {
if (uwsgi.shared->spooler_signal_pipe[0] != -1)
close(uwsgi.shared->spooler_signal_pipe[0]);
if (uwsgi.shared->spooler_signal_pipe[1] != -1)
close(uwsgi.shared->spooler_signal_pipe[1]);
}
#endif
if (uwsgi.shared->mule_signal_pipe[0] != -1)
close(uwsgi.shared->mule_signal_pipe[0]);
if (muleid == 0) {
if (uwsgi.shared->mule_signal_pipe[1] != -1)
close(uwsgi.shared->mule_signal_pipe[1]);
if (uwsgi.shared->mule_queue_pipe[1] != -1)
close(uwsgi.shared->mule_queue_pipe[1]);
}
for (i = 0; i < uwsgi.mules_cnt; i++) {
if (uwsgi.mules[i].signal_pipe[0] != -1)
close(uwsgi.mules[i].signal_pipe[0]);
if (muleid != i + 1) {
if (uwsgi.mules[i].signal_pipe[1] != -1)
close(uwsgi.mules[i].signal_pipe[1]);
if (uwsgi.mules[i].queue_pipe[1] != -1)
close(uwsgi.mules[i].queue_pipe[1]);
}
}
for (i = 0; i < uwsgi.farms_cnt; i++) {
if (uwsgi.farms[i].signal_pipe[0] != -1)
close(uwsgi.farms[i].signal_pipe[0]);
if (muleid == 0) {
if (uwsgi.farms[i].signal_pipe[1] != -1)
close(uwsgi.farms[i].signal_pipe[1]);
if (uwsgi.farms[i].queue_pipe[1] != -1)
close(uwsgi.farms[i].queue_pipe[1]);
}
}
}
}
int uwsgi_respawn_worker(int wid) {
int respawns = uwsgi.workers[wid].respawn_count;
int i;
if (uwsgi.threaded_logger) {
pthread_mutex_lock(&uwsgi.threaded_logger_lock);
}
pid_t pid = uwsgi_fork(uwsgi.workers[wid].name);
if (pid == 0) {
signal(SIGWINCH, worker_wakeup);
signal(SIGTSTP, worker_wakeup);
uwsgi.mywid = wid;
uwsgi.mypid = getpid();
uwsgi.workers[uwsgi.mywid].pid = uwsgi.mypid;
uwsgi.workers[uwsgi.mywid].id = uwsgi.mywid;
uwsgi.workers[uwsgi.mywid].harakiri = 0;
uwsgi.workers[uwsgi.mywid].rss_size = 0;
uwsgi.workers[uwsgi.mywid].vsz_size = 0;
// do not reset worker counters on reload !!!
//uwsgi.workers[uwsgi.mywid].requests = 0;
// ...but maintain a delta counter (yes this is racy in multithread)
uwsgi.workers[uwsgi.mywid].delta_requests = 0;
//uwsgi.workers[uwsgi.mywid].failed_requests = 0;
uwsgi.workers[uwsgi.mywid].respawn_count++;
uwsgi.workers[uwsgi.mywid].last_spawn = uwsgi.current_time;
uwsgi.workers[uwsgi.mywid].manage_next_request = 1;
uwsgi.workers[uwsgi.mywid].cheaped = 0;
uwsgi.workers[uwsgi.mywid].busy = 0;
uwsgi.workers[uwsgi.mywid].suspended = 0;
uwsgi.workers[uwsgi.mywid].sig = 0;
// reset the apps count with a copy from the master
uwsgi.workers[uwsgi.mywid].apps_cnt = uwsgi.workers[0].apps_cnt;
uwsgi_fixup_fds(wid, 0, NULL);
uwsgi.my_signal_socket = uwsgi.workers[wid].signal_pipe[1];
if (uwsgi.master_process) {
if ((uwsgi.workers[uwsgi.mywid].respawn_count || uwsgi.cheap)) {
for (i = 0; i < 256; i++) {
if (uwsgi.p[i]->master_fixup) {
uwsgi.p[i]->master_fixup(1);
}
}
}
}
return 1;
}
else if (pid < 1) {
uwsgi_error("fork()");
}
else {
if (respawns > 0) {
uwsgi_log("Respawned uWSGI worker %d (new pid: %d)\n", wid, (int) pid);
}
else {
uwsgi_log("spawned uWSGI worker %d (pid: %d, cores: %d)\n", wid, pid, uwsgi.cores);
}
}
if (uwsgi.threaded_logger) {
pthread_mutex_unlock(&uwsgi.threaded_logger_lock);
}
return 0;
}
void uwsgi_manage_signal_cron(time_t now) {
struct tm *uwsgi_cron_delta;
int i;
uwsgi_cron_delta = localtime(&now);
if (uwsgi_cron_delta) {
// fix month
uwsgi_cron_delta->tm_mon++;
uwsgi_lock(uwsgi.cron_table_lock);
for (i = 0; i < ushared->cron_cnt; i++) {
struct uwsgi_cron *ucron = &ushared->cron[i];
int uc_minute, uc_hour, uc_day, uc_month, uc_week;
uc_minute = ucron->minute;
uc_hour = ucron->hour;
uc_day = ucron->day;
uc_month = ucron->month;
uc_week = ucron->week;
if (ucron->minute == -1)
uc_minute = uwsgi_cron_delta->tm_min;
if (ucron->hour == -1)
uc_hour = uwsgi_cron_delta->tm_hour;
if (ucron->month == -1)
uc_month = uwsgi_cron_delta->tm_mon;
if (ucron->day == -1)
uc_day = uwsgi_cron_delta->tm_mday;
if (ucron->week == -1)
uc_week = uwsgi_cron_delta->tm_wday;
int run_task = 0;
// mday and wday are ORed
if (ucron->day != -1 && ucron->week != -1) {
if (uwsgi_cron_delta->tm_min == uc_minute && uwsgi_cron_delta->tm_hour == uc_hour && uwsgi_cron_delta->tm_mon == uc_month && (uwsgi_cron_delta->tm_mday == uc_day || uwsgi_cron_delta->tm_wday == uc_week)) {
run_task = 1;
}
}
else {
if (uwsgi_cron_delta->tm_min == uc_minute && uwsgi_cron_delta->tm_hour == uc_hour && uwsgi_cron_delta->tm_mon == uc_month && uwsgi_cron_delta->tm_mday == uc_day && uwsgi_cron_delta->tm_wday == uc_week) {
run_task = 1;
}
}
if (run_task == 1) {
// date match, signal it ?
if (now - ucron->last_job > 60) {
uwsgi_route_signal(ucron->sig);
ucron->last_job = now;
}
}
}
uwsgi_unlock(uwsgi.cron_table_lock);
}
else {
uwsgi_error("localtime()");
}
}
void uwsgi_manage_command_cron(time_t now) {
struct tm *uwsgi_cron_delta;
struct uwsgi_cron *current_cron = uwsgi.crons;
int uc_minute, uc_hour, uc_day, uc_month, uc_week;
uwsgi_cron_delta = localtime(&now);
if (!uwsgi_cron_delta) {
uwsgi_error("localtime()");
return;
}
while (current_cron) {
uc_minute = current_cron->minute;
uc_hour = current_cron->hour;
uc_day = current_cron->day;
uc_month = current_cron->month;
uc_week = current_cron->week;
if (current_cron->minute == -1)
uc_minute = uwsgi_cron_delta->tm_min;
if (current_cron->hour == -1)
uc_hour = uwsgi_cron_delta->tm_hour;
if (current_cron->month == -1)
uc_month = uwsgi_cron_delta->tm_mon;
if (current_cron->day == -1)
uc_day = uwsgi_cron_delta->tm_mday;
if (current_cron->week == -1)
uc_week = uwsgi_cron_delta->tm_wday;
int run_task = 0;
// mday and wday are ORed
if (current_cron->day != -1 && current_cron->week != -1) {
if (uwsgi_cron_delta->tm_min == uc_minute && uwsgi_cron_delta->tm_hour == uc_hour && uwsgi_cron_delta->tm_mon == uc_month && (uwsgi_cron_delta->tm_mday == uc_day || uwsgi_cron_delta->tm_wday == uc_week)) {
run_task = 1;
}
}
else {
if (uwsgi_cron_delta->tm_min == uc_minute && uwsgi_cron_delta->tm_hour == uc_hour && uwsgi_cron_delta->tm_mon == uc_month && uwsgi_cron_delta->tm_mday == uc_day && uwsgi_cron_delta->tm_wday == uc_week) {
run_task = 1;
}
}
if (run_task == 1) {
// date match, run command ?
if (now - current_cron->last_job > 60) {
//call command
if (current_cron->command) {
if (uwsgi_run_command(current_cron->command, NULL, -1) >= 0) {
uwsgi_log_verbose("[uWSGI-cron] running %s\n", current_cron->command);
}
}
current_cron->last_job = now;
}
}
current_cron = current_cron->next;
}
}
#define stats_send_llu(x, y) fprintf(output, x, (long long unsigned int) y)
#define stats_send(x, y) fprintf(output, x, y)
void uwsgi_send_stats(int fd) {
int i, j;
struct sockaddr_un client_src;
struct uwsgi_app *ua;
socklen_t client_src_len = 0;
int client_fd = accept(fd, (struct sockaddr *) &client_src, &client_src_len);
if (client_fd < 0) {
uwsgi_error("accept()");
return;
}
FILE *output = fdopen(client_fd, "w");
if (!output) {
uwsgi_error("fdopen()");
close(client_fd);
return;
}
stats_send("{ \"version\": \"%s\",\n", UWSGI_VERSION);
#ifdef __linux__
stats_send_llu("\"listen_queue\": %llu,\n", (unsigned long long) uwsgi.shared->options[UWSGI_OPTION_BACKLOG_STATUS]);
stats_send_llu("\"listen_queue_errors\": %llu,\n", (unsigned long long) uwsgi.shared->options[UWSGI_OPTION_BACKLOG_ERRORS]);
#endif
stats_send_llu("\"load\": %llu,\n", uwsgi.shared->load);
fprintf(output, "\"pid\": %d,\n", (int) (getpid()));
fprintf(output, "\"uid\": %d,\n", (int) (getuid()));
fprintf(output, "\"gid\": %d,\n", (int) (getgid()));
char *cwd = uwsgi_get_cwd();
stats_send("\"cwd\": \"%s\",\n", cwd);
free(cwd);
if (uwsgi.daemons) {
fprintf(output, "\"daemons\": [\n");
struct uwsgi_daemon *ud = uwsgi.daemons;
while (ud) {
fprintf(output, "\t{ \"cmd\": \"%s\", \"pid\": %d, \"respawns\": %llu }", ud->command, (int) ud->pid, (unsigned long long) ud->respawns - 1);
if (ud->next)
fprintf(output, ",\n");
else {
fprintf(output, "\n");
}
ud = ud->next;
}
fprintf(output, "],\n");
}
fprintf(output, "\"locks\": [\n");
struct uwsgi_lock_item *uli = uwsgi.registered_locks;
while (uli) {
if (uli->next) {
fprintf(output, "\t{ \"%s\": %d },\n", uli->id, (int) uli->pid);
}
else {
fprintf(output, "\t{ \"%s\": %d }\n", uli->id, (int) uli->pid);
}
uli = uli->next;
}
fprintf(output, "],\n");
fprintf(output, "\"workers\": [\n");
for (i = 0; i < uwsgi.numproc; i++) {
fprintf(output, "\t{");
fprintf(output, "\"id\": %d, ", uwsgi.workers[i + 1].id);
fprintf(output, "\"pid\": %d, ", (int) uwsgi.workers[i + 1].pid);
stats_send_llu("\"requests\": %llu, ", uwsgi.workers[i + 1].requests);
stats_send_llu("\"delta_requests\": %llu, ", uwsgi.workers[i + 1].delta_requests);
stats_send_llu("\"exceptions\": %llu, ", uwsgi.workers[i + 1].exceptions);
stats_send_llu("\"signals\": %llu, ", uwsgi.workers[i + 1].signals);
if (uwsgi.workers[i + 1].cheaped) {
fprintf(output, "\"status\": \"cheap\", ");
}
else if (uwsgi.workers[i + 1].suspended && !uwsgi.workers[i + 1].busy) {
fprintf(output, "\"status\": \"pause\", ");
}
else {
if (uwsgi.workers[i + 1].sig) {
fprintf(output, "\"status\": \"sig%d\", ", uwsgi.workers[i + 1].signum);
}
else if (uwsgi.workers[i + 1].busy) {
fprintf(output, "\"status\": \"busy\", ");
}
else {
fprintf(output, "\"status\": \"idle\", ");
}
}
stats_send_llu("\"rss\": %llu, ", uwsgi.workers[i + 1].rss_size);
stats_send_llu("\"vsz\": %llu, ", uwsgi.workers[i + 1].vsz_size);
stats_send_llu("\"running_time\": %llu, ", uwsgi.workers[i + 1].running_time);
stats_send_llu("\"last_spawn\": %llu, ", uwsgi.workers[i + 1].last_spawn);
stats_send_llu("\"respawn_count\": %llu, ", uwsgi.workers[i + 1].respawn_count);
stats_send_llu("\"tx\": %llu, ", uwsgi.workers[i + 1].tx);
stats_send_llu("\"avg_rt\": %llu, ", uwsgi.workers[i + 1].avg_response_time);
fprintf(output, "\"apps\": [\n");
for (j = 0; j < uwsgi.workers[i + 1].apps_cnt; j++) {
ua = &uwsgi.workers[i + 1].apps[j];
fprintf(output, "\t\t{ ");
fprintf(output, "\"id\": %d, ", j);
fprintf(output, "\"modifier1\": %d, ", ua->modifier1);
fprintf(output, "\"mountpoint\": \"%.*s\", ", ua->mountpoint_len, ua->mountpoint);
stats_send_llu("\"startup_time\": %llu, ", ua->startup_time);
stats_send_llu("\"requests\": %llu, ", ua->requests);
stats_send_llu("\"exceptions\": %llu, ", ua->exceptions);
if (ua->chdir) {
fprintf(output, "\"chdir\": \"%s\", ", ua->chdir);
}
else {
fprintf(output, "\"chdir\": \"\" ");
}
if (j == uwsgi.workers[i + 1].apps_cnt - 1) {
fprintf(output, "}\n");
}
else {
fprintf(output, "},\n");
}
}
fprintf(output, "\t\t]");
if (i == uwsgi.numproc - 1) {
fprintf(output, "}\n");
}
else {
fprintf(output, "},\n");
}
}
fprintf(output, "]}\n");
fclose(output);
}
void uwsgi_register_cheaper_algo(char *name, int(*func) (void)) {
struct uwsgi_cheaper_algo *uca = uwsgi.cheaper_algos;
if (!uca) {
uwsgi.cheaper_algos = uwsgi_malloc(sizeof(struct uwsgi_cheaper_algo));
uca = uwsgi.cheaper_algos;
}
else {
while (uca) {
if (!uca->next) {
uca->next = uwsgi_malloc(sizeof(struct uwsgi_cheaper_algo));
uca = uca->next;
break;
}
uca = uca->next;
}
}
uca->name = name;
uca->func = func;
uca->next = NULL;
#ifdef UWSGI_DEBUG
uwsgi_log("[uwsgi-cheaper-algo] registered \"%s\"\n", uca->name);
#endif
}