From: Jérôme Loyet Date: Sat, 2 Jul 2011 16:10:18 +0000 (+0000) Subject: - Implemented FR #54577 (Enhanced status page with full status and details about... X-Git-Tag: php-5.3.7RC3~33 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=217382d4efd634e10fb054ae0fddfd1cd41babae;p=php - Implemented FR #54577 (Enhanced status page with full status and details about each processes - Added a web page (status.html) for real-time FPM status - Fixed missing Expires and Cache-Control headers for ping and status pages --- diff --git a/NEWS b/NEWS index d0f503c039..79be61e8bd 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,13 @@ PHP NEWS - PDO ODBC driver: . Fixed data type usage in 64bit. (leocsilva at gmail dot com) +- PHP-FPM SAPI: + . Implemented FR #54577 (Enhanced status page with full status and details + about each processes. Also provide a web page (status.html) for + real-time FPM status. (fat) + . Fixed missing Expires and Cache-Control headers for ping and status pages. + (fat) + - SPL extension: . Fixed bug #54971 (Wrong result when using iterator_to_array with use_keys on true). (Pierrick) diff --git a/sapi/fpm/Makefile.frag b/sapi/fpm/Makefile.frag index 0859ef10ca..85a0ba70f9 100644 --- a/sapi/fpm/Makefile.frag +++ b/sapi/fpm/Makefile.frag @@ -26,3 +26,6 @@ install-fpm: install-sapi @$(mkinstalldirs) $(INSTALL_ROOT)$(mandir)/man8 @$(INSTALL_DATA) sapi/fpm/php-fpm.8 $(INSTALL_ROOT)$(mandir)/man8/php-fpm$(program_suffix).8 + @echo "Installing PHP FPM status page: $(INSTALL_ROOT)$(datadir)/fpm/" + @$(mkinstalldirs) $(INSTALL_ROOT)$(datadir)/fpm + @$(INSTALL_DATA) sapi/fpm/status.html $(INSTALL_ROOT)$(datadir)/fpm/status.html diff --git a/sapi/fpm/fpm/fpm.c b/sapi/fpm/fpm/fpm.c index b0b3b2b7a4..13106dfe4d 100644 --- a/sapi/fpm/fpm/fpm.c +++ b/sapi/fpm/fpm/fpm.c @@ -49,7 +49,6 @@ int fpm_init(int argc, char **argv, char *config, char *prefix, int test_conf) / if (0 > fpm_php_init_main() || 0 > fpm_stdio_init_main() || - 0 > fpm_log_init_main() || 0 > fpm_conf_init_main(test_conf) || 0 > fpm_unix_init_main() || 0 > fpm_scoreboard_init_main() || diff --git a/sapi/fpm/fpm/fpm_children.c b/sapi/fpm/fpm/fpm_children.c index 05d741cb89..65f56e7269 100644 --- a/sapi/fpm/fpm/fpm_children.c +++ b/sapi/fpm/fpm/fpm_children.c @@ -344,7 +344,8 @@ static void fpm_child_resources_use(struct fpm_child_s *child) /* {{{ */ } fpm_scoreboard_free(wp->scoreboard); } - fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, child->pid); + + fpm_scoreboard_child_use(child->wp->scoreboard, child->scoreboard_i, getpid()); fpm_stdio_child_use_pipes(child); fpm_child_free(child); } diff --git a/sapi/fpm/fpm/fpm_log.c b/sapi/fpm/fpm/fpm_log.c index ef1c0c3be3..5360c45f7a 100644 --- a/sapi/fpm/fpm/fpm_log.c +++ b/sapi/fpm/fpm/fpm_log.c @@ -29,9 +29,6 @@ static char *fpm_log_format = NULL; static int fpm_log_fd = -1; -#ifdef HAVE_TIMES -static float tick; -#endif int fpm_log_open(int reopen) /* {{{ */ { @@ -67,24 +64,6 @@ int fpm_log_open(int reopen) /* {{{ */ } /* }}} */ -int fpm_log_init_main() /* {{{ */ -{ -#ifdef HAVE_TIMES -#if (defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)) - tick = sysconf(_SC_CLK_TCK); -#else /* _SC_CLK_TCK */ -#ifdef HZ - tick = HZ; -#else /* HZ */ - tick = 100; -#endif /* HZ */ -#endif /* _SC_CLK_TCK */ - zlog(ZLOG_DEBUG, "got clock tick '%.0f'", tick); -#endif /* HAVE_TIMES */ - return 0; -} -/* }}} */ - /* }}} */ int fpm_log_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ { @@ -122,7 +101,6 @@ int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */ size_t len, len2; struct fpm_scoreboard_proc_s proc, *proc_p; struct fpm_scoreboard_s *scoreboard; - struct timeval uptime, now; char tmp[129]; char format[129]; time_t now_epoch; @@ -141,7 +119,6 @@ int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */ test = 1; } - fpm_clock_get(&now); now_epoch = time(NULL); if (!test) { @@ -157,8 +134,6 @@ int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */ } proc = *proc_p; fpm_scoreboard_proc_release(proc_p); - - timersub(&now, &proc.accepted, &uptime); } token = 0; @@ -198,19 +173,15 @@ int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */ case 'C': /* %CPU */ if (format[0] == '\0' || !strcasecmp(format, "total")) { if (!test) { - tms_total = - (proc.cpu_finished.tms_utime + proc.cpu_finished.tms_stime + proc.cpu_finished.tms_cutime + proc.cpu_finished.tms_cstime) - - - (proc.cpu_accepted.tms_utime + proc.cpu_accepted.tms_stime + proc.cpu_accepted.tms_cutime + proc.cpu_accepted.tms_cstime) - ; + tms_total = proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cutime + proc.last_request_cpu.tms_cstime; } } else if (!strcasecmp(format, "user")) { if (!test) { - tms_total = (proc.cpu_finished.tms_utime + proc.cpu_finished.tms_cutime) - (proc.cpu_accepted.tms_utime + proc.cpu_accepted.tms_cutime); + tms_total = proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_cutime; } } else if (!strcasecmp(format, "system")) { if (!test) { - tms_total = (proc.cpu_finished.tms_stime + proc.cpu_finished.tms_cstime) - (proc.cpu_accepted.tms_stime + proc.cpu_accepted.tms_cstime); + tms_total = proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cstime; } } else { zlog(ZLOG_WARNING, "only 'total', 'user' or 'system' are allowed as a modifier for %%%c ('%s')", *s, format); @@ -219,7 +190,7 @@ int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */ format[0] = '\0'; if (!test) { - len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.2f", tms_total / tick / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.); + len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.2f", tms_total / fpm_scoreboard_get_tick() / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.); } break; #endif @@ -228,19 +199,19 @@ int fpm_log_write(char *log_format TSRMLS_DC) /* {{{ */ /* seconds */ if (format[0] == '\0' || !strcasecmp(format, "seconds")) { if (!test) { - len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", uptime.tv_sec + uptime.tv_usec / 1000000.); + len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", proc.duration.tv_sec + proc.duration.tv_usec / 1000000.); } /* miliseconds */ } else if (!strcasecmp(format, "miliseconds") || !strcasecmp(format, "mili")) { if (!test) { - len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", uptime.tv_sec * 1000. + uptime.tv_usec / 1000.); + len2 = snprintf(b, FPM_LOG_BUFFER - len, "%.3f", proc.duration.tv_sec * 1000. + proc.duration.tv_usec / 1000.); } /* microseconds */ } else if (!strcasecmp(format, "microseconds") || !strcasecmp(format, "micro")) { if (!test) { - len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", uptime.tv_sec * 1000000UL + uptime.tv_usec); + len2 = snprintf(b, FPM_LOG_BUFFER - len, "%lu", proc.duration.tv_sec * 1000000UL + proc.duration.tv_usec); } } else { diff --git a/sapi/fpm/fpm/fpm_log.h b/sapi/fpm/fpm/fpm_log.h index 74f842646f..f0199d9cb6 100644 --- a/sapi/fpm/fpm/fpm_log.h +++ b/sapi/fpm/fpm/fpm_log.h @@ -6,7 +6,6 @@ #define FPM_LOG_H 1 #include "fpm_worker_pool.h" -int fpm_log_init_main(); int fpm_log_init_child(struct fpm_worker_pool_s *wp); int fpm_log_write(char *log_format TSRMLS_DC); int fpm_log_open(int reopen); diff --git a/sapi/fpm/fpm/fpm_request.c b/sapi/fpm/fpm/fpm_request.c index e0d1815dbe..f94196b562 100644 --- a/sapi/fpm/fpm/fpm_request.c +++ b/sapi/fpm/fpm/fpm_request.c @@ -23,6 +23,19 @@ #include "zlog.h" +static const char *requests_stages[] = { + [FPM_REQUEST_ACCEPTING] = "Idle", + [FPM_REQUEST_READING_HEADERS] = "Reading headers", + [FPM_REQUEST_INFO] = "Getting request informations", + [FPM_REQUEST_EXECUTING] = "Running", + [FPM_REQUEST_END] = "Ending", + [FPM_REQUEST_FINISHED] = "Finishing", +}; + +const char *fpm_request_get_stage_name(int stage) { + return requests_stages[stage]; +} + void fpm_request_accepting() /* {{{ */ { struct fpm_scoreboard_proc_s *proc; @@ -38,10 +51,6 @@ void fpm_request_accepting() /* {{{ */ proc->request_stage = FPM_REQUEST_ACCEPTING; proc->tv = now; - proc->request_uri[0] = '\0'; - proc->request_method[0] = '\0'; - proc->script_filename[0] = '\0'; - proc->content_length = 0; fpm_scoreboard_proc_release(proc); /* idle++, active-- */ @@ -78,6 +87,12 @@ void fpm_request_reading_headers() /* {{{ */ #ifdef HAVE_TIMES proc->cpu_accepted = cpu; #endif + proc->requests++; + proc->request_uri[0] = '\0'; + proc->request_method[0] = '\0'; + proc->script_filename[0] = '\0'; + proc->query_string[0] = '\0'; + proc->content_length = 0; fpm_scoreboard_proc_release(proc); /* idle--, active++, request++ */ @@ -176,9 +191,13 @@ void fpm_request_end(TSRMLS_D) /* {{{ */ } proc->request_stage = FPM_REQUEST_FINISHED; proc->tv = now; + timersub(&now, &proc->accepted, &proc->duration); #ifdef HAVE_TIMES - proc->cpu_finished = cpu; timersub(&proc->tv, &proc->accepted, &proc->cpu_duration); + proc->last_request_cpu.tms_utime = cpu.tms_utime - proc->cpu_accepted.tms_utime; + proc->last_request_cpu.tms_stime = cpu.tms_stime - proc->cpu_accepted.tms_stime; + proc->last_request_cpu.tms_cutime = cpu.tms_cutime - proc->cpu_accepted.tms_cutime; + proc->last_request_cpu.tms_cstime = cpu.tms_cstime - proc->cpu_accepted.tms_cstime; #endif proc->memory = memory; fpm_scoreboard_proc_release(proc); diff --git a/sapi/fpm/fpm/fpm_request.h b/sapi/fpm/fpm/fpm_request.h index b3014a5f1d..05cca228b1 100644 --- a/sapi/fpm/fpm/fpm_request.h +++ b/sapi/fpm/fpm/fpm_request.h @@ -17,6 +17,7 @@ struct timeval; void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *tv, int terminate_timeout, int slowlog_timeout); int fpm_request_is_idle(struct fpm_child_s *child); +const char *fpm_request_get_stage_name(int stage); enum fpm_request_stage_e { FPM_REQUEST_ACCEPTING = 1, diff --git a/sapi/fpm/fpm/fpm_scoreboard.c b/sapi/fpm/fpm/fpm_scoreboard.c index a68e13ae8d..a980df9106 100644 --- a/sapi/fpm/fpm/fpm_scoreboard.c +++ b/sapi/fpm/fpm/fpm_scoreboard.c @@ -17,12 +17,30 @@ static struct fpm_scoreboard_s *fpm_scoreboard = NULL; static int fpm_scoreboard_i = -1; +#ifdef HAVE_TIMES +static float fpm_scoreboard_tick; +#endif + int fpm_scoreboard_init_main() /* {{{ */ { struct fpm_worker_pool_s *wp; int i; +#ifdef HAVE_TIMES +#if (defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)) + fpm_scoreboard_tick = sysconf(_SC_CLK_TCK); +#else /* _SC_CLK_TCK */ +#ifdef HZ + fpm_scoreboard_tick = HZ; +#else /* HZ */ + fpm_scoreboard_tick = 100; +#endif /* HZ */ +#endif /* _SC_CLK_TCK */ + zlog(ZLOG_DEBUG, "got clock tick '%.0f'", fpm_scoreboard_tick); +#endif /* HAVE_TIMES */ + + for (wp = fpm_worker_all_pools; wp; wp = wp->next) { if (wp->config->pm_max_children < 1) { zlog(ZLOG_ERROR, "[pool %s] Unable to create scoreboard SHM because max_client is not set", wp->config->name); @@ -40,7 +58,11 @@ int fpm_scoreboard_init_main() /* {{{ */ } wp->scoreboard->nprocs = wp->config->pm_max_children; for (i=0; iscoreboard->nprocs; i++) { - wp->scoreboard->procs[i] = NULL; + wp->scoreboard->procs[i] = fpm_shm_alloc(sizeof(struct fpm_scoreboard_proc_s)); + if (!wp->scoreboard->procs[i]) { + return -1; + } + memset(wp->scoreboard->procs[i], 0, sizeof(struct fpm_scoreboard_proc_s)); } wp->scoreboard->pm = wp->config->pm; @@ -229,6 +251,7 @@ void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_ind return; } proc->pid = pid; + proc->start_epoch = time(NULL); } /* }}} */ @@ -242,9 +265,8 @@ void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_ind return; } - if (scoreboard->procs[child_index]) { - fpm_shm_free(scoreboard->procs[child_index], sizeof(struct fpm_scoreboard_proc_s)); - scoreboard->procs[child_index] = NULL; + if (scoreboard->procs[child_index] && scoreboard->procs[child_index]->used > 0) { + memset(scoreboard->procs[child_index], 0, sizeof(struct fpm_scoreboard_proc_s)); } /* set this slot as free to avoid search on next alloc */ @@ -262,7 +284,7 @@ int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_in /* first try the slot which is supposed to be free */ if (scoreboard->free_proc >= 0 && scoreboard->free_proc < scoreboard->nprocs) { - if (!scoreboard->procs[scoreboard->free_proc]) { + if (scoreboard->procs[scoreboard->free_proc] && !scoreboard->procs[scoreboard->free_proc]->used) { i = scoreboard->free_proc; } } @@ -270,7 +292,7 @@ int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_in if (i < 0) { /* the supposed free slot is not, let's search for a free slot */ zlog(ZLOG_DEBUG, "[pool %s] the proc->free_slot was not free. Let's search", scoreboard->pool); for (i=0; inprocs; i++) { - if (!scoreboard->procs[i]) { /* found */ + if (scoreboard->procs[i] && !scoreboard->procs[i]->used) { /* found */ break; } } @@ -282,10 +304,7 @@ int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_in return -1; } - scoreboard->procs[i] = fpm_shm_alloc(sizeof(struct fpm_scoreboard_proc_s)); - if (!scoreboard->procs[i]) { - return -1; - } + scoreboard->procs[i]->used = 1; *child_index = i; /* supposed next slot is free */ @@ -299,3 +318,11 @@ int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_in } /* }}} */ +#ifdef HAVE_TIMES +float fpm_scoreboard_get_tick() /* {{{ */ +{ + return fpm_scoreboard_tick; +} +/* }}} */ +#endif + diff --git a/sapi/fpm/fpm/fpm_scoreboard.h b/sapi/fpm/fpm/fpm_scoreboard.h index 00860878cb..136ea481a4 100644 --- a/sapi/fpm/fpm/fpm_scoreboard.h +++ b/sapi/fpm/fpm/fpm_scoreboard.h @@ -22,9 +22,13 @@ struct fpm_scoreboard_proc_s { atomic_t lock; char dummy[16]; }; + int used; + time_t start_epoch; pid_t pid; + unsigned long requests; enum fpm_request_stage_e request_stage; struct timeval accepted; + struct timeval duration; time_t accepted_epoch; struct timeval tv; char request_uri[128]; @@ -35,8 +39,9 @@ struct fpm_scoreboard_proc_s { char auth_user[32]; #ifdef HAVE_TIMES struct tms cpu_accepted; - struct tms cpu_finished; struct timeval cpu_duration; + struct tms last_request_cpu; + struct timeval last_request_cpu_duration; #endif size_t memory; }; @@ -81,4 +86,8 @@ void fpm_scoreboard_child_use(struct fpm_scoreboard_s *scoreboard, int child_ind void fpm_scoreboard_proc_free(struct fpm_scoreboard_s *scoreboard, int child_index); int fpm_scoreboard_proc_alloc(struct fpm_scoreboard_s *scoreboard, int *child_index); +#ifdef HAVE_TIMES +float fpm_scoreboard_get_tick(); +#endif + #endif diff --git a/sapi/fpm/fpm/fpm_status.c b/sapi/fpm/fpm/fpm_status.c index 9fab7e7fb3..ac9365de8a 100644 --- a/sapi/fpm/fpm/fpm_status.c +++ b/sapi/fpm/fpm/fpm_status.c @@ -13,6 +13,7 @@ #include "fpm_scoreboard.h" #include "zlog.h" #include "fpm_atomic.h" +#include static char *fpm_status_uri = NULL; static char *fpm_status_ping_uri = NULL; @@ -46,9 +47,12 @@ int fpm_status_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ { struct fpm_scoreboard_s scoreboard, *scoreboard_p; -// struct fpm_scoreboard_proc_s proc; - char *buffer, *syntax, *time_format, time_buffer[64]; + struct fpm_scoreboard_proc_s proc; + char *buffer, *time_format, time_buffer[64]; time_t now_epoch; + int full, encode; + char *short_syntax, *short_post; + char *full_pre, *full_syntax, *full_post, *full_separator; if (!SG(request_info).request_uri) { return 0; @@ -56,7 +60,10 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ /* PING */ if (fpm_status_ping_uri && fpm_status_ping_response && !strcmp(fpm_status_ping_uri, SG(request_info).request_uri)) { + fpm_request_executing(); sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC); SG(sapi_headers).http_response_code = 200; PUTS(fpm_status_ping_response); return 1; @@ -64,12 +71,15 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ /* STATUS */ if (fpm_status_uri && !strcmp(fpm_status_uri, SG(request_info).request_uri)) { + fpm_request_executing(); scoreboard_p = fpm_scoreboard_get(); if (!scoreboard_p) { zlog(ZLOG_ERROR, "status: unable to find or access status shared memory"); SG(sapi_headers).http_response_code = 500; sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC); PUTS("Internal error. Please review log file for errors."); return 1; } @@ -78,6 +88,8 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ zlog(ZLOG_NOTICE, "[pool %s] status: scoreboard already in used.", scoreboard_p->pool); SG(sapi_headers).http_response_code = 503; sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC); PUTS("Server busy. Please try again later."); return 1; } @@ -89,38 +101,99 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ zlog(ZLOG_ERROR, "[pool %s] invalid status values", scoreboard.pool); SG(sapi_headers).http_response_code = 500; sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC); PUTS("Internal error. Please review log file for errors."); return 1; } + /* full status ? */ + full = SG(request_info).request_uri && strstr(SG(request_info).query_string, "full"); + short_syntax = short_post = NULL; + full_separator = full_pre = full_syntax = full_post = NULL; + encode = 0; + /* HTML */ if (SG(request_info).query_string && strstr(SG(request_info).query_string, "html")) { sapi_add_header_ex(ZEND_STRL("Content-Type: text/html"), 1, 1 TSRMLS_CC); time_format = "%d/%b/%Y:%H:%M:%S %z"; - syntax = + encode = 1; + + short_syntax = + "\n" + "\n" + "PHP-FPM Status Page\n" + "\n" "\n" - "\n" - "\n" - "\n" - "\n" - "\n" + "\n" + "\n" + "\n" + "\n" + "\n" +#if HAVE_FPM_LQ + "\n" + "\n" + "\n" +#endif + "\n" + "\n" + "\n" + "\n" + "\n" + "
pool%s
process manager%s
start time%s
start since%lu
accepted conn%lu
pool%s
process manager%s
start time%s
start since%lu
accepted conn%lu
listen queue%u
max listen queue%u
listen queue len%d
idle processes%d
active processes%d
total processes%d
max active processes%d
max children reached%u
\n"; + + if (!full) { + short_post = ""; + } else { + full_pre = + "\n" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" #if HAVE_FPM_LQ - "\n" - "\n" - "\n" + "" #endif - "\n" - "\n" - "\n" - "\n" - "\n" - "
pidstatestart timestart sincerequestsrequest durationrequest methodrequest uricontent lengthuserscript
listen queue%u
max listen queue%u
listen queue len%d
last request cpu
idle processes%d
active processes%d
total processes%d
max active processes%d
max children reached%u
"; + "last request memory" + "\n"; + + full_syntax = + "" + "%d" + "%s" + "%s" + "%lu" + "%lu" + "%lu" + "%s" + "%s%s%s" + "%zu" + "%s" + "%s" +#if HAVE_FPM_LQ + "%.2f" +#endif + "%zu" + "\n"; + + full_post = ""; + } /* XML */ } else if (SG(request_info).request_uri && strstr(SG(request_info).query_string, "xml")) { sapi_add_header_ex(ZEND_STRL("Content-Type: text/xml"), 1, 1 TSRMLS_CC); time_format = "%s"; - syntax = + encode = 1; + + short_syntax = "\n" "\n" "%s\n" @@ -137,14 +210,40 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ "%d\n" "%d\n" "%d\n" - "%u\n" - ""; + "%u\n"; + + if (!full) { + short_post = ""; + } else { + full_pre = "\n"; + full_syntax = + "" + "%d" + "%s" + "%s" + "%lu" + "%lu" + "%lu" + "%s" + "%s%s%s" + "%zu" + "%s" + "" +#if HAVE_FPM_LQ + "%.2f" +#endif + "%zu" + "\n" + ; + full_post = "\n"; + } /* JSON */ } else if (SG(request_info).request_uri && strstr(SG(request_info).query_string, "json")) { sapi_add_header_ex(ZEND_STRL("Content-Type: application/json"), 1, 1 TSRMLS_CC); time_format = "%s"; - syntax = + + short_syntax = "{" "\"pool\":\"%s\"," "\"process manager\":\"%s\"," @@ -160,14 +259,41 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ "\"active processes\":%d," "\"total processes\":%d," "\"max active processes\":%d," - "\"max children reached\":%u" - "}"; + "\"max children reached\":%u"; + + if (!full) { + short_post = "}"; + } else { + full_separator = ","; + full_pre = ", \"processes\":["; + + full_syntax = "{" + "\"pid\":%d," + "\"state\":\"%s\"," + "\"start time\":%s," + "\"start since\":%lu," + "\"requests\":%lu," + "\"request duration\":%lu," + "\"request method\":\"%s\"," + "\"request uri\":\"%s%s%s\"," + "\"content length\":%zu," + "\"user\":\"%s\"," + "\"script\":\"%s\"," +#if HAVE_FPM_LQ + "\"last request cpu\":%.2f," +#endif + "\"last request memory\":%zu" + "}"; + + full_post = "]}"; + } /* TEXT */ } else { sapi_add_header_ex(ZEND_STRL("Content-Type: text/plain"), 1, 1 TSRMLS_CC); time_format = "%d/%b/%Y:%H:%M:%S %z"; - syntax = + + short_syntax = "pool: %s\n" "process manager: %s\n" "start time: %s\n" @@ -183,11 +309,35 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ "total processes: %d\n" "max active processes: %d\n" "max children reached: %u\n"; + + if (full) { + full_syntax = + "\n" + "************************\n" + "pid: %d\n" + "state: %s\n" + "start time: %s\n" + "start since: %lu\n" + "requests: %lu\n" + "request duration: %lu\n" + "request method: %s\n" + "request URI: %s%s%s\n" + "content length: %zu\n" + "user: %s\n" + "script: %s\n" +#ifdef HAVE_FPM_LQ + "last request cpu: %.2f\n" +#endif + "last request memory: %zu\n"; + } } + sapi_add_header_ex(ZEND_STRL("Expires: Thu, 01 Jan 1970 00:00:00 GMT"), 1, 1 TSRMLS_CC); + sapi_add_header_ex(ZEND_STRL("Cache-Control: no-cache, no-store, must-revalidate, max-age=0"), 1, 1 TSRMLS_CC); + strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&scoreboard.start_epoch)); now_epoch = time(NULL); - spprintf(&buffer, 0, syntax, + spprintf(&buffer, 0, short_syntax, scoreboard.pool, scoreboard.pm == PM_STYLE_STATIC ? "static" : "dynamic", time_buffer, @@ -208,6 +358,96 @@ int fpm_status_handle_request(TSRMLS_D) /* {{{ */ PUTS(buffer); efree(buffer); + if (short_post) { + PUTS(short_post); + } + + /* no need to test the var 'full' */ + if (full_syntax) { + int i, len, first; + char *query_string; + struct timeval duration, now; +#ifdef HAVE_FPM_LQ + float cpu; +#endif + + fpm_clock_get(&now); + + if (full_pre) { + PUTS(full_pre); + } + + first = 1; + for (i=0; inprocs; i++) { + if (!scoreboard_p->procs[i] || !scoreboard_p->procs[i]->used) { + continue; + } + proc = *scoreboard_p->procs[i]; + + if (first) { + first = 0; + } else { + if (full_separator) { + PUTS(full_separator); + } + } + + query_string = NULL; + len = 0; + if (proc.query_string[0] != '\0') { + if (!encode) { + query_string = proc.query_string; + } else { + query_string = php_escape_html_entities_ex((unsigned char *)proc.query_string, strlen(proc.query_string), &len, 1, ENT_HTML_IGNORE_ERRORS & ENT_COMPAT, NULL, 1 TSRMLS_CC); + } + } + +#ifdef HAVE_FPM_LQ + /* prevent NaN */ + if (proc.cpu_duration.tv_sec == 0 && proc.cpu_duration.tv_usec == 0) { + cpu = 0.; + } else { + cpu = (proc.last_request_cpu.tms_utime + proc.last_request_cpu.tms_stime + proc.last_request_cpu.tms_cutime + proc.last_request_cpu.tms_cstime) / fpm_scoreboard_get_tick() / (proc.cpu_duration.tv_sec + proc.cpu_duration.tv_usec / 1000000.) * 100.; + } +#endif + + if (proc.request_stage == FPM_REQUEST_ACCEPTING) { + duration = proc.duration; + } else { + timersub(&now, &proc.accepted, &duration); + } + strftime(time_buffer, sizeof(time_buffer) - 1, time_format, localtime(&proc.start_epoch)); + spprintf(&buffer, 0, full_syntax, + proc.pid, + fpm_request_get_stage_name(proc.request_stage), + time_buffer, + now_epoch - proc.start_epoch, + proc.requests, + duration.tv_sec * 1000000UL + duration.tv_usec, + proc.request_method[0] != '\0' ? proc.request_method : "-", + proc.request_uri[0] != '\0' ? proc.request_uri : "-", + query_string ? "?" : "", + query_string ? query_string : "", + proc.content_length, + proc.auth_user[0] != '\0' ? proc.auth_user : "-", + proc.script_filename[0] != '\0' ? proc.script_filename : "-", +#ifdef HAVE_FPM_LQ + proc.request_stage == FPM_REQUEST_ACCEPTING ? cpu : 0., +#endif + proc.request_stage == FPM_REQUEST_ACCEPTING ? proc.memory : 0); + PUTS(buffer); + efree(buffer); + + if (len > 0 && query_string) { + efree(query_string); + } + } + + if (full_post) { + PUTS(full_post); + } + } + return 1; } diff --git a/sapi/fpm/php-fpm.conf.in b/sapi/fpm/php-fpm.conf.in index 1d6f2c66d6..6cd6c1bf52 100644 --- a/sapi/fpm/php-fpm.conf.in +++ b/sapi/fpm/php-fpm.conf.in @@ -182,34 +182,98 @@ pm.max_children = 50 ;pm.max_requests = 500 ; The URI to view the FPM status page. If this value is not set, no URI will be -; recognized as a status page. By default, the status page shows the following -; information: -; accepted conn - the number of request accepted by the pool; +; recognized as a status page. It shows the following informations: ; pool - the name of the pool; ; process manager - static or dynamic; +; start time - the date and time FPM has started; +; start since - number of seconds since FPM has started; +; accepted conn - the number of request accepted by the pool; +; listen queue - the number of request in the queue of pending +; connections (see backlog in listen(2)); +; max listen queue - the maximum number of requests in the queue +; of pending connections since FPM has started; +; listen queue len - the size of the socket queue of pending connections; ; idle processes - the number of idle processes; ; active processes - the number of active processes; -; total processes - the number of idle + active processes. +; total processes - the number of idle + active processes; +; max active processes - the maximum number of active processes since FPM +; has started; ; max children reached - number of times, the process limit has been reached, ; when pm tries to start more children (works only for -; pm 'dynamic') -; The values of 'idle processes', 'active processes' and 'total processes' are -; updated each second. The value of 'accepted conn' is updated in real time. +; pm 'dynamic'); +; Value are updated in real time. ; Example output: -; accepted conn: 12073 ; pool: www ; process manager: static -; idle processes: 35 -; active processes: 65 -; total processes: 100 -; max children reached: 1 +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 62636 +; accepted conn: 190460 +; listen queue: 0 +; max listen queue: 1 +; listen queue len: 42 +; idle processes: 4 +; active processes: 11 +; total processes: 15 +; max active processes: 12 +; max children reached: 0 +; ; By default the status page output is formatted as text/plain. Passing either -; 'html', 'xml' or 'json' as a query string will return the corresponding output -; syntax. Example: +; 'html', 'xml' or 'json' in the query string will return the corresponding +; output syntax. Example: ; http://www.foo.bar/status ; http://www.foo.bar/status?json ; http://www.foo.bar/status?html ; http://www.foo.bar/status?xml +; +; By default the status page only outputs short status. Passing 'full' in the +; query string will also return status for each pool process. +; Example: +; http://www.foo.bar/status?full +; http://www.foo.bar/status?json&full +; http://www.foo.bar/status?html&full +; http://www.foo.bar/status?xml&full +; The Full status returns for each process: +; pid - the PID of the process; +; state - the state of the process (Idle, Running, ...); +; start time - the date and time the process has started; +; start since - the number of seconds since the process has started; +; requests - the number of requests the process has served; +; request duration - the duration in µs of the requests; +; request method - the request method (GET, POST, ...); +; request URI - the request URI with the query string; +; content length - the content length of the request (only with POST); +; user - the user (PHP_AUTH_USER) (or '-' if not set); +; script - the main script called (or '-' if not set); +; last request cpu - the %cpu the last request consumed +; it's always 0 if the process is not in Idle state +; because CPU calculation is done when the request +; processing has terminated; +; last request memory - the max amount of memory the last request consumed +; it's always 0 if the process is not in Idle state +; because memory calculation is done when the request +; processing has terminated; +; If the process is in Idle state, then informations are related to the +; last request the process has served. Otherwise informations are related to +; the current request being served. +; Example output: +; ************************ +; pid: 31330 +; state: Running +; start time: 01/Jul/2011:17:53:49 +0200 +; start since: 63087 +; requests: 12808 +; request duration: 1250261 +; request method: GET +; request URI: /test_mem.php?N=10000 +; content length: 0 +; user: - +; script: /home/fat/web/docs/php/test_mem.php +; last request cpu: 0.00 +; last request memory: 0 +; +; Note: There is a real-time FPM status monitoring sample web page available +; It's available in: @EXPANDED_DATADIR@/fpm/status.html +; ; Note: The value must start with a leading slash (/). The value can be ; anything, but it may not be a good idea to use the .php extension or it ; may conflict with a real PHP file. diff --git a/sapi/fpm/status.html b/sapi/fpm/status.html new file mode 100644 index 0000000000..86492d73df --- /dev/null +++ b/sapi/fpm/status.html @@ -0,0 +1,459 @@ + + + + + + + PHP-FPM status page + + +
+ + + + +
+ PHP Logo

PHP-FPM real-time status page

+
+
+ + + + + + + + +
Status URL
Ajax status
Refresh Rate
Actions + + +
+

Pool Status

+ + +
 
+

Active Processes status

+ + +
PID↓Start TimeStart SinceRequests ServedRequest DurationRequest methodRequest URIContent LengthUserScript
+

Idle Processes status

+ + +
PID↓Start TimeStart SinceRequests ServedRequest DurationRequest methodRequest URIContent LengthUserScriptLast Request %CPULast Request Memory
+
+

+ + Valid XHTML 1.0 Transitional + +

+ + +