pax_global_header00006660000000000000000000000064121330665750014522gustar00rootroot0000000000000052 comment=07f72a7abebacd56fc78da1074fc2d89d846d77e php-pinba-1.0.0/000077500000000000000000000000001213306657500133765ustar00rootroot00000000000000php-pinba-1.0.0/package.xml000066400000000000000000000070701213306657500155170ustar00rootroot00000000000000 pinba pecl.php.net Pinba Client extension for Pinba statistics server. Antony Dovgal tony2001 tony2001@php.net yes 2012-08-17 1.0.0 1.0.0 stable stable PHP - Fixed 5_4 build. - Fixed several minor memleaks. - Added optional flag to pinba_flush() to flush only stopped timers (use PINBA_FLUSH_ONLY_STOPPED_TIMERS). 4.4.8 6.0.0 6.0.0 1.4.0b1 pinba 2010-11-26 0.0.6 0.0.6 beta beta PHP - Added pinba_timer_delete() function. - Added pinba_hostname_set() function. - Added experimental IPv6 support. (Florian Forster) - Fixed extension crash on empty tag value. 2009-10-19 0.0.5 0.0.5 beta beta PHP - Added rusage to timers (not sent to the server, to be discussed) beta beta 0.0.4 0.0.4 2009-08-26 - Added HTTP response status to the response data. - Added support of Google Protocol Buffers 2.1.0+. - Added pinba_script_name_set() function. beta beta 0.0.3 0.0.3 2009-05-04 - Initial release. php-pinba-1.0.0/pinba-1.0.0/000077500000000000000000000000001213306657500151215ustar00rootroot00000000000000php-pinba-1.0.0/pinba-1.0.0/CREDITS000066400000000000000000000000051213306657500161340ustar00rootroot00000000000000pinbaphp-pinba-1.0.0/pinba-1.0.0/NEWS000066400000000000000000000015701213306657500156230ustar00rootroot00000000000000Pinba 1.0.0 17 Aug 2012 ---------------------------- - Fixed 5_4 build. - Fixed several minor memleaks. - Added optional flag to pinba_flush() to flush only stopped timers (use PINBA_FLUSH_ONLY_STOPPED_TIMERS). Pinba 0.0.6 26 Nov 2010 ---------------------------- - Added pinba_timer_delete() function. - Added pinba_hostname_set() function. - Added experimental IPv6 support. (Florian Forster) - Fixed extension crash on empty tag value. Pinba 0.0.5 19 Oct 2009 ---------------------------- - Added rusage to timers (not aggregated on server (yet?)). Pinba extension 0.0.4 26 Aug 2009 -------------------------------------- - Added HTTP response status to the response data. - Added support of Google Protocol Buffers 2.1.0+. - Added pinba_script_name_set() function. Pinba extension 0.0.3 04 May 2009 -------------------------------------- Initial release. php-pinba-1.0.0/pinba-1.0.0/README000066400000000000000000000000551213306657500160010ustar00rootroot00000000000000PHP extension for Pinba See http://pinba.org php-pinba-1.0.0/pinba-1.0.0/config.m4000066400000000000000000000057331213306657500166400ustar00rootroot00000000000000dnl $Id: config.m4,v 1.2.4.2 2009/03/06 11:12:55 tony Exp $ PHP_ARG_WITH(pinba, for Pinba support, [ --with-pinba[=DIR] Include Pinba support. DIR is Google Protocol Buffers install prefix.]) if test "$PHP_PINBA" != "no"; then SEARCH_PATH="/usr /usr/local /opt" SEARCH_FOR="/include/google/protobuf/descriptor.h" if test -d "$PHP_PINBA"; then AC_MSG_CHECKING([for Google Protocol Buffers files in $PHP_PINBA]) if test -r "$PHP_PINBA/$SEARCH_FOR"; then PROTOBUF_DIR=$PHP_PINBA AC_MSG_RESULT([found]) fi else AC_MSG_CHECKING([for Google Protocol Buffers files in default path]) for i in $SEARCH_PATH ; do if test -r $i/$SEARCH_FOR; then PROTOBUF_DIR=$i AC_MSG_RESULT(found in $i) fi done fi if test -z "$PROTOBUF_DIR"; then AC_MSG_RESULT([not found]) AC_MSG_ERROR([Unable to find Google Protocol Buffers headers]) fi PHP_ADD_INCLUDE($PROTOBUF_DIR/include) AC_MSG_CHECKING([for Google Protobuf compiler]) PROTOC="x" if ! test -x "$PROTOBUF_DIR/bin/protoc"; then AC_MSG_ERROR([Unable to find Protobuf compiler (protoc) in $PROTOBUF_DIR/bin]) else PROTOC="$PROTOBUF_DIR/bin/protoc" AC_MSG_RESULT([$PROTOC]) fi AC_MSG_CHECKING([for Google Protobuf version]) gpb_full_version=`$PROTOC --version | $SED -e 's/libprotoc //'` ac_IFS=$IFS IFS="." set $gpb_full_version IFS=$ac_IFS GPB_VERSION=`expr [$]1 \* 1000000 + [$]2 \* 1000 + [$]3` AC_MSG_RESULT([$gpb_full_version]) dnl Google Protobuf >= 2.1.0 uses pthread_once() in its headers if test "$GPB_VERSION" -ge "2001000"; then old_LDFLAGS=$LDFLAGS old_LIBS=$LIBS LIBS= AC_SEARCH_LIBS([pthread_once], [pthread c_r c], [ pthread_once=none ], [ AC_MSG_ERROR([pthread_once() is required for Google Protocol Buffers >= 2.1.0, but it could not be found, exiting]) ]) if test "$ac_cv_search_pthread_once" != "none required" && test "$ac_cv_search_pthread_once" != "no"; then LDFLAGS="$old_LDFLAGS $ac_cv_search_pthread_once" else LDFLAGS="$old_LDFLAGS" fi LIBS="$old_LIBS" fi if test "x$PHP_LIBDIR" = "x"; then PHP_LIBDIR=lib fi PHP_REQUIRE_CXX PHP_ADD_LIBRARY_WITH_PATH(stdc++, "", PINBA_SHARED_LIBADD) PHP_ADD_LIBRARY_WITH_PATH(protobuf, $PROTOBUF_DIR/$PHP_LIBDIR, PINBA_SHARED_LIBADD) PHP_SUBST(PINBA_SHARED_LIBADD) PHP_NEW_EXTENSION(pinba, pinba.cc pinba-pb.cc, $ext_shared,, -DNDEBUG) AC_MSG_WARN([Regenerating protocol code]) `$PROTOC -I$ext_srcdir $ext_srcdir/pinba.proto --cpp_out=$ext_srcdir` if test "$?" != 0; then AC_MSG_ERROR([Failed to regenerate protocol code]) fi `$SED -e 's/pinba\.pb\.h/pinba-pb.h/' $ext_srcdir/pinba.pb.cc > $ext_srcdir/pinba-pb.cc && rm $ext_srcdir/pinba.pb.cc` if test "$?" != 0; then AC_MSG_ERROR([Failed to run sed]) fi `mv $ext_srcdir/pinba.pb.h $ext_srcdir/pinba-pb.h` if test "$?" != 0; then AC_MSG_ERROR([Failed to rename pinba.pb.h to pinba-pb.h]) fi fi php-pinba-1.0.0/pinba-1.0.0/php_pinba.h000066400000000000000000000046441213306657500172420ustar00rootroot00000000000000/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | +----------------------------------------------------------------------+ $Id: php_pinba.h,v 1.9.2.3 2009/04/28 10:40:57 tony Exp $ */ #ifndef PHP_PINBA_H #define PHP_PINBA_H #include extern zend_module_entry pinba_module_entry; #define phpext_pinba_ptr &pinba_module_entry #ifdef ZTS #include "TSRM.h" #endif #define PINBA_COLLECTOR_DEFAULT_PORT "30002" #define PHP_PINBA_VERSION "1.0.0" typedef struct _pinba_req_data { /* {{{ */ char *server_name; char *script_name; unsigned int req_count; unsigned int doc_size; unsigned int mem_peak_usage; struct timeval req_start; struct timeval req_time; struct timeval ru_utime; struct timeval ru_stime; } pinba_req_data; /* }}} */ ZEND_BEGIN_MODULE_GLOBALS(pinba) /* {{{ */ struct sockaddr_storage collector_sockaddr; size_t collector_sockaddr_len; char *collector_address; char *server_host; char *server_port; int (*old_sapi_ub_write) (const char *, unsigned int TSRMLS_DC); char host_name[128]; char *server_name; char *script_name; HashTable timers; pinba_req_data tmp_req_data; zend_bool timers_stopped; zend_bool enabled; ZEND_END_MODULE_GLOBALS(pinba) /* }}} */ #ifdef ZTS #define PINBA_G(v) TSRMG(pinba_globals_id, zend_pinba_globals *, v) #else #define PINBA_G(v) (pinba_globals.v) #endif #endif /* PHP_PINBA_H */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * indent-tabs-mode: t * End: */ php-pinba-1.0.0/pinba-1.0.0/pinba.cc000066400000000000000000001147731213306657500165360ustar00rootroot00000000000000/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2008 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.php.net/license/2_02.txt. | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Antony Dovgal | | Florian Forster (IPv6 support) | +----------------------------------------------------------------------+ $Id: pinba.cc,v 1.1.2.9 2009/04/28 10:46:55 tony Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif extern "C" { #include #include #include #include #include #include #include #include #include #include #include "php.h" #include "php_ini.h" #include "SAPI.h" #include "ext/standard/info.h" #include "ext/standard/php_array.h" } #include "php_pinba.h" #include using namespace std; #include "pinba-pb.h" ZEND_DECLARE_MODULE_GLOBALS(pinba) #ifdef COMPILE_DL_PINBA BEGIN_EXTERN_C() ZEND_GET_MODULE(pinba) END_EXTERN_C() #endif static int le_pinba_timer; static int pinba_socket = -1; #if ZEND_MODULE_API_NO > 20020429 #define ONUPDATELONGFUNC OnUpdateLong #else #define ONUPDATELONGFUNC OnUpdateInt #endif #define PINBA_FLUSH_ONLY_STOPPED_TIMERS (1<<0) typedef struct _pinba_timer_tag { /* {{{ */ char *name; int name_len; int name_id; char *value; /* we cast all types to string */ int value_len; int value_id; } pinba_timer_tag_t; /* }}} */ typedef struct _pinba_timer { /* {{{ */ int rsrc_id; unsigned int started:1; unsigned int hit_count; pinba_timer_tag_t **tags; int tags_num; struct { int tv_sec; int tv_usec; } start; struct { int tv_sec; int tv_usec; } value; zval *data; struct timeval tmp_ru_utime; struct timeval tmp_ru_stime; struct timeval ru_utime; struct timeval ru_stime; int deleted:1; } pinba_timer_t; /* }}} */ #define PHP_ZVAL_TO_TIMER(zval, timer) \ ZEND_FETCH_RESOURCE(timer, pinba_timer_t *, &zval, -1, "pinba timer", le_pinba_timer) #define timeval_cvt(a, b) do { (a)->tv_sec = (b)->tv_sec; (a)->tv_usec = (b)->tv_usec; } while (0); #define timeval_to_float(t) (float)(t).tv_sec + (float)(t).tv_usec / 1000000.0 /* {{{ internal funcs */ static inline int php_pinba_timer_stop(pinba_timer_t *t) /* {{{ */ { struct timeval now; struct rusage u, tmp; if (!t->started) { return FAILURE; } gettimeofday(&now, 0); timersub(&now, &t->start, &t->value); if (getrusage(RUSAGE_SELF, &u) == 0) { timersub(&u.ru_utime, &t->tmp_ru_utime, &tmp.ru_utime); timersub(&u.ru_stime, &t->tmp_ru_stime, &tmp.ru_stime); timeradd(&t->ru_utime, &tmp.ru_utime, &t->ru_utime); timeradd(&t->ru_stime, &tmp.ru_stime, &t->ru_stime); } t->started = 0; return SUCCESS; } /* }}} */ static inline void php_pinba_timer_tags_dtor(pinba_timer_tag_t **tags, int tags_num) /* {{{ */ { int i; pinba_timer_tag_t *tag; for (i = 0; i < tags_num; i++) { tag = *(tags + i); if (tag) { if (tag->name) { efree(tag->name); } if (tag->value) { efree(tag->value); } efree(tag); } } } /* }}} */ static void php_pinba_timer_dtor(pinba_timer_t *t) /* {{{ */ { php_pinba_timer_tags_dtor(t->tags, t->tags_num); efree(t->tags); } /* }}} */ static void php_timer_hash_dtor(void *data) /* {{{ */ { pinba_timer_t *t = *(pinba_timer_t **)data; if (t) { php_pinba_timer_dtor(t); efree(t); *(pinba_timer_t **)data = NULL; } } /* }}} */ static int php_pinba_tags_to_hashed_string(pinba_timer_t *timer, char **hashed_tags, int *hashed_tags_len TSRMLS_DC) /* {{{ */ { int i; char *buf; int buf_len, wrote_len; *hashed_tags = NULL; *hashed_tags_len = 0; if (!timer->tags_num) { return FAILURE; } buf_len = 4096; wrote_len = 0; buf = (char *)emalloc(buf_len + 1); for (i = 0; i < timer->tags_num; i++) { if (buf_len <= (wrote_len + timer->tags[i]->name_len + 2 + timer->tags[i]->value_len + 1)) { buf_len = wrote_len + timer->tags[i]->name_len + 2 + timer->tags[i]->value_len + 1 + 4096; buf = (char *)erealloc(buf, buf_len + 1); } memcpy(buf + wrote_len, timer->tags[i]->name, timer->tags[i]->name_len); wrote_len += timer->tags[i]->name_len; memcpy(buf + wrote_len, "=>", 2); wrote_len += 2; memcpy(buf + wrote_len, timer->tags[i]->value, timer->tags[i]->value_len); wrote_len += timer->tags[i]->value_len; memcpy(buf + wrote_len, ",", 1); wrote_len += 1; } buf[wrote_len] = '\0'; *hashed_tags = buf; *hashed_tags_len = wrote_len; return SUCCESS; } /* }}} */ static void php_timer_resource_dtor(zend_rsrc_list_entry *entry TSRMLS_DC) /* {{{ */ { pinba_timer_t *t = (pinba_timer_t *)entry->ptr; php_pinba_timer_stop(t); /* php_pinba_timer_dtor(t); all timers are destroyed at once */ /* but we don't need the user data anymore */ if (t->data) { zval_ptr_dtor(&t->data); t->data = NULL; } if (!t->deleted) { if (zend_hash_index_exists(&PINBA_G(timers), t->rsrc_id) == 0) { zend_hash_index_update(&PINBA_G(timers), t->rsrc_id, &t, sizeof(pinba_timer_t *), NULL); } } else { php_pinba_timer_dtor(t); efree(t); } } /* }}} */ static int php_pinba_timer_stop_helper(zend_rsrc_list_entry *le, void *le_type TSRMLS_DC) /* {{{ */ { if (le->type == le_pinba_timer) { /* remove not looking at the refcount */ return ZEND_HASH_APPLY_REMOVE; } return ZEND_HASH_APPLY_KEEP; } /* }}} */ static int sapi_ub_write_counter(const char *str, unsigned int length TSRMLS_DC) /* {{{ */ { PINBA_G(tmp_req_data).doc_size += length; return PINBA_G(old_sapi_ub_write)(str, length TSRMLS_CC); } /* }}} */ static int php_pinba_init_socket (TSRMLS_D) /* {{{ */ { struct addrinfo *ai_list; struct addrinfo *ai_ptr; struct addrinfo ai_hints; int fd; int status; if (PINBA_G(server_host) == NULL || PINBA_G(server_port) == NULL) { return -1; } memset(&ai_hints, 0, sizeof(ai_hints)); ai_hints.ai_flags = 0; #ifdef AI_ADDRCONFIG ai_hints.ai_flags |= AI_ADDRCONFIG; #endif ai_hints.ai_family = AF_UNSPEC; ai_hints.ai_socktype = SOCK_DGRAM; ai_hints.ai_addr = NULL; ai_hints.ai_canonname = NULL; ai_hints.ai_next = NULL; ai_list = NULL; status = getaddrinfo(PINBA_G(server_host), PINBA_G(server_port), &ai_hints, &ai_list); if (status != 0) { /* TODO: error reporting */ return -1; } fd = -1; for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) { fd = socket(ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol); if (fd < 0) { continue; } if (pinba_socket >= 0) { close(pinba_socket); } pinba_socket = fd; memcpy(&PINBA_G(collector_sockaddr), ai_ptr->ai_addr, ai_ptr->ai_addrlen); PINBA_G(collector_sockaddr_len) = ai_ptr->ai_addrlen; break; } freeaddrinfo(ai_list); return ((fd >= 0) ? 0 : -1); } /* }}} */ static inline int php_pinba_req_data_send(pinba_req_data record, HashTable *timers, long flags TSRMLS_DC) /* {{{ */ { int ret = SUCCESS, timers_num, dict_cnt = 0; HashTable dict; HashTable timers_uniq; HashPosition pos; Pinba::Request *request; int i; /* no socket -> bail out */ if (pinba_socket < 0) { return FAILURE; } timers_num = zend_hash_num_elements(timers); if (timers_num > 0) { pinba_timer_t *t, *old_t, **t_el, **old_t_el; char *hashed_tags; int hashed_tags_len; /* make sure we send aggregated timers to the server */ zend_hash_init(&timers_uniq, 10, NULL, NULL, 0); zend_hash_init(&dict, 10, NULL, NULL, 0); for (zend_hash_internal_pointer_reset_ex(timers, &pos); zend_hash_get_current_data_ex(timers, (void **) &t_el, &pos) == SUCCESS; zend_hash_move_forward_ex(timers, &pos)) { t = *t_el; if (php_pinba_tags_to_hashed_string(t, &hashed_tags, &hashed_tags_len TSRMLS_CC) != SUCCESS) { continue; } /* aggregate only stopped timers */ if ((flags & PINBA_FLUSH_ONLY_STOPPED_TIMERS) != 0 && t->started) { continue; } if (zend_hash_find(&timers_uniq, hashed_tags, hashed_tags_len + 1, (void **)&old_t_el) == SUCCESS) { old_t = *old_t_el; timeradd(&old_t->value, &t->value, &old_t->value); old_t->hit_count++; } else { zend_hash_add(&timers_uniq, hashed_tags, hashed_tags_len + 1, t_el, sizeof(pinba_timer_t *), NULL); } efree(hashed_tags); } /* create our temporary dictionary and add ids to timers */ for (zend_hash_internal_pointer_reset_ex(&timers_uniq, &pos); zend_hash_get_current_data_ex(&timers_uniq, (void **) &t_el, &pos) == SUCCESS; zend_hash_move_forward_ex(&timers_uniq, &pos)) { t = *t_el; for (i = 0; i < t->tags_num; i++) { int *id; if (zend_hash_find(&dict, t->tags[i]->name, t->tags[i]->name_len + 1, (void **)&id) != SUCCESS) { ret = zend_hash_add(&dict, t->tags[i]->name, t->tags[i]->name_len + 1, &dict_cnt, sizeof(int), NULL); if (ret != SUCCESS) { break; } t->tags[i]->name_id = dict_cnt; dict_cnt++; } else { t->tags[i]->name_id = *id; } if (zend_hash_find(&dict, t->tags[i]->value, t->tags[i]->value_len + 1, (void **)&id) != SUCCESS) { ret = zend_hash_add(&dict, t->tags[i]->value, t->tags[i]->value_len + 1, &dict_cnt, sizeof(int), NULL); if (ret != SUCCESS) { break; } t->tags[i]->value_id = dict_cnt; dict_cnt++; } else { t->tags[i]->value_id = *id; } } } } request = new Pinba::Request; request->set_hostname(PINBA_G(host_name)); request->set_server_name(record.server_name); request->set_script_name(record.script_name); request->set_request_count(record.req_count); request->set_document_size(record.doc_size); request->set_memory_peak(record.mem_peak_usage); request->set_request_time(timeval_to_float(record.req_time)); request->set_ru_utime(timeval_to_float(record.ru_utime)); request->set_ru_stime(timeval_to_float(record.ru_stime)); request->set_status(SG(sapi_headers).http_response_code); /* timers */ if (timers_num > 0) { pinba_timer_t *t, **t_el; int *id; for (zend_hash_internal_pointer_reset_ex(&dict, &pos); zend_hash_get_current_data_ex(&dict, (void **) &id, &pos) == SUCCESS; zend_hash_move_forward_ex(&dict, &pos)) { char *str; uint str_len; ulong dummy; if (zend_hash_get_current_key_ex(&dict, &str, &str_len, &dummy, 0, &pos) == HASH_KEY_IS_STRING) { request->add_dictionary(str); } else { continue; } } zend_hash_destroy(&dict); for (zend_hash_internal_pointer_reset_ex(&timers_uniq, &pos); zend_hash_get_current_data_ex(&timers_uniq, (void **) &t_el, &pos) == SUCCESS; zend_hash_move_forward_ex(&timers_uniq, &pos)) { t = *t_el; for (i = 0; i < t->tags_num; i++) { request->add_timer_tag_name(t->tags[i]->name_id); request->add_timer_tag_value(t->tags[i]->value_id); } request->add_timer_tag_count(i); request->add_timer_hit_count(t->hit_count); request->add_timer_value(timeval_to_float(t->value)); } zend_hash_destroy(&timers_uniq); } if (ret == SUCCESS) { size_t total_sent = 0; ssize_t sent; string data; bool res; res = request->SerializeToString(&data); while (total_sent < data.size()) { int flags = 0; sent = sendto(pinba_socket, data.c_str() + total_sent, data.size() - total_sent, flags, (struct sockaddr *) &PINBA_G(collector_sockaddr), PINBA_G(collector_sockaddr_len)); if (sent < 0) { ret = FAILURE; break; } total_sent += sent; } } else { ret = FAILURE; } delete request; return ret; } /* }}} */ static inline void php_pinba_req_data_dtor(pinba_req_data *record) /* {{{ */ { if (record->server_name) { efree(record->server_name); } if (record->script_name) { efree(record->script_name); } memset(record, 0, sizeof(pinba_req_data)); } /* }}} */ static void php_pinba_flush_data(const char *custom_script_name, long flags TSRMLS_DC) /* {{{ */ { struct timeval req_finish; struct rusage u; pinba_req_data req_data; int status; #if PHP_MAJOR_VERSION >= 5 PINBA_G(tmp_req_data).mem_peak_usage = zend_memory_peak_usage(1 TSRMLS_CC); #elif PHP_MAJOR_VERSION == 4 && MEMORY_LIMIT PINBA_G(tmp_req_data).mem_peak_usage = AG(allocated_memory_peak); #else /* no data available */ PINBA_G(tmp_req_data).mem_peak_usage = 0; #endif if ((flags & PINBA_FLUSH_ONLY_STOPPED_TIMERS) == 0) { /* stop all running timers */ zend_hash_apply_with_argument(&EG(regular_list), (apply_func_arg_t) php_pinba_timer_stop_helper, (void *)(long)le_pinba_timer TSRMLS_CC); } /* prevent any further access to the timers */ PINBA_G(timers_stopped) = 1; if (!PINBA_G(enabled) || !PINBA_G(server_host) || !PINBA_G(server_port)) { /* no server host/port set, exit */ zend_hash_clean(&PINBA_G(timers)); PINBA_G(timers_stopped) = 0; return; } status = php_pinba_init_socket(TSRMLS_C); if (status != 0) { PINBA_G(timers_stopped) = 0; return; } /* compute how many time the request took */ if (gettimeofday(&req_finish, 0) == 0) { timersub(&req_finish, &(PINBA_G(tmp_req_data).req_start), &req_data.req_time); } /* get rusage */ if (getrusage(RUSAGE_SELF, &u) == 0) { timersub(&u.ru_utime, &(PINBA_G(tmp_req_data).ru_utime), &req_data.ru_utime); timersub(&u.ru_stime, &(PINBA_G(tmp_req_data).ru_stime), &req_data.ru_stime); } timeval_cvt(&req_data.req_start, &PINBA_G(tmp_req_data).req_start); req_data.req_count = ++PINBA_G(tmp_req_data).req_count; req_data.doc_size = PINBA_G(tmp_req_data).doc_size; req_data.mem_peak_usage = PINBA_G(tmp_req_data).mem_peak_usage; req_data.server_name = NULL; req_data.script_name = NULL; if (PINBA_G(server_name)) { req_data.server_name = estrdup(PINBA_G(server_name)); } if (!req_data.server_name) { req_data.server_name = estrdup("unknown"); } if (custom_script_name) { req_data.script_name = estrdup(custom_script_name); } else if (PINBA_G(script_name)) { req_data.script_name = estrdup(PINBA_G(script_name)); } if (!req_data.script_name) { req_data.script_name = estrdup("unknown"); } php_pinba_req_data_send(req_data, &PINBA_G(timers), flags TSRMLS_CC); php_pinba_req_data_dtor(&req_data); PINBA_G(timers_stopped) = 0; zend_hash_clean(&PINBA_G(timers)); } /* }}} */ static int php_pinba_key_compare(const void *a, const void *b TSRMLS_DC) /* {{{ */ { Bucket *f; Bucket *s; zval result; zval first; zval second; f = *((Bucket **) a); s = *((Bucket **) b); if (f->nKeyLength == 0) { Z_TYPE(first) = IS_LONG; Z_LVAL(first) = f->h; } else { Z_TYPE(first) = IS_STRING; Z_STRVAL(first) = (char *)f->arKey; Z_STRLEN(first) = f->nKeyLength - 1; } if (s->nKeyLength == 0) { Z_TYPE(second) = IS_LONG; Z_LVAL(second) = s->h; } else { Z_TYPE(second) = IS_STRING; Z_STRVAL(second) = (char *)s->arKey; Z_STRLEN(second) = s->nKeyLength - 1; } if (string_compare_function(&result, &first, &second TSRMLS_CC) == FAILURE) { return 0; } if (Z_TYPE(result) == IS_DOUBLE) { if (Z_DVAL(result) < 0) { return -1; } else if (Z_DVAL(result) > 0) { return 1; } else { return 0; } } convert_to_long(&result); if (Z_LVAL(result) < 0) { return -1; } else if (Z_LVAL(result) > 0) { return 1; } return 0; } /* }}} */ static int php_pinba_array_to_tags(zval *array, pinba_timer_tag_t ***tags TSRMLS_DC) /* {{{ */ { int num, i = 0; zval **value; char *tag_name, *value_str; uint tag_name_len, value_str_len; ulong dummy; num = zend_hash_num_elements(Z_ARRVAL_P(array)); if (!num) { return FAILURE; } /* sort array, we'll use this when computing tags hash */ zend_hash_sort(Z_ARRVAL_P(array), zend_qsort, php_pinba_key_compare, 0 TSRMLS_CC); *tags = (pinba_timer_tag_t **)ecalloc(num, sizeof(pinba_timer_tag_t *)); for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(array)); zend_hash_get_current_data(Z_ARRVAL_P(array), (void **) &value) == SUCCESS; zend_hash_move_forward(Z_ARRVAL_P(array))) { switch (Z_TYPE_PP(value)) { case IS_NULL: case IS_STRING: case IS_BOOL: case IS_LONG: case IS_DOUBLE: SEPARATE_ZVAL(value); convert_to_string_ex(value); value_str = estrndup(Z_STRVAL_PP(value), Z_STRLEN_PP(value)); value_str_len = Z_STRLEN_PP(value); break; default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "tags cannot have non-scalar values"); php_pinba_timer_tags_dtor(*tags, i); efree(*tags); return FAILURE; } if (zend_hash_get_current_key_ex(Z_ARRVAL_P(array), &tag_name, &tag_name_len, &dummy, 1, NULL) == HASH_KEY_IS_STRING) { (*tags)[i] = (pinba_timer_tag_t *)emalloc(sizeof(pinba_timer_tag_t)); (*tags)[i]->name = tag_name; (*tags)[i]->name_len = tag_name_len - 1; (*tags)[i]->value = value_str; (*tags)[i]->value_len = value_str_len; } else { if (value_str) { efree(value_str); } php_error_docref(NULL TSRMLS_CC, E_WARNING, "tags can only have string names (i.e. tags array cannot contain numeric indexes)"); php_pinba_timer_tags_dtor(*tags, i); efree(*tags); return FAILURE; } i++; } return SUCCESS; } /* }}} */ static pinba_timer_t *php_pinba_timer_ctor(pinba_timer_tag_t **tags, int tags_num TSRMLS_DC) /* {{{ */ { struct timeval now; pinba_timer_t *t; t = (pinba_timer_t *)ecalloc(1, sizeof(pinba_timer_t)); t->tags_num = tags_num; t->tags = tags; gettimeofday(&now, 0); timeval_cvt(&t->start, &now); return t; } /* }}} */ static void php_pinba_get_timer_info(pinba_timer_t *t, zval *info TSRMLS_DC) /* {{{ */ { zval *tags; pinba_timer_tag_t *tag; struct timeval tmp; int i; array_init(info); if (t->started) { gettimeofday(&tmp, 0); timersub(&tmp, &t->start, &tmp); timeradd(&t->value, &tmp, &tmp); } else { timeval_cvt(&tmp, &t->value); } add_assoc_double_ex(info, "value", sizeof("value"), timeval_to_float(tmp)); MAKE_STD_ZVAL(tags); array_init(tags); for (i = 0; i < t->tags_num; i++) { tag = t->tags[i]; add_assoc_stringl_ex(tags, tag->name, tag->name_len + 1, tag->value, tag->value_len, 1); } add_assoc_zval_ex(info, "tags", sizeof("tags"), tags); add_assoc_bool_ex(info, "started", sizeof("started"), t->started ? 1 : 0); if (t->data) { add_assoc_zval_ex(info, "data", sizeof("data"), t->data); zval_add_ref(&t->data); } else { add_assoc_null_ex(info, "data", sizeof("data")); } add_assoc_double_ex(info, "ru_utime", sizeof("ru_utime"), timeval_to_float(t->ru_utime)); add_assoc_double_ex(info, "ru_stime", sizeof("ru_stime"), timeval_to_float(t->ru_stime)); } /* }}} */ /* }}} */ /* {{{ proto resource pinba_timer_start(array tags[, array data]) Start user timer */ static PHP_FUNCTION(pinba_timer_start) { zval *tags_array, *data = NULL; pinba_timer_t *t = NULL; pinba_timer_tag_t **tags; int tags_num; struct rusage u; if (PINBA_G(timers_stopped)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "all timers have already been stopped"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|a", &tags_array, &data) != SUCCESS) { return; } tags_num = zend_hash_num_elements(Z_ARRVAL_P(tags_array)); if (!tags_num) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "tags array cannot be empty"); RETURN_FALSE; } if (php_pinba_array_to_tags(tags_array, &tags TSRMLS_CC) != SUCCESS) { RETURN_FALSE; } t = php_pinba_timer_ctor(tags, tags_num TSRMLS_CC); if (data) { MAKE_STD_ZVAL(t->data); *(t->data) = *data; zval_copy_ctor(t->data); INIT_PZVAL(t->data); } t->started = 1; t->hit_count = 1; #if PHP_VERSION_ID >= 50400 t->rsrc_id = zend_list_insert(t, le_pinba_timer TSRMLS_CC); #else t->rsrc_id = zend_list_insert(t, le_pinba_timer); #endif if (getrusage(RUSAGE_SELF, &u) == 0) { timeval_cvt(&t->tmp_ru_utime, &u.ru_utime); timeval_cvt(&t->tmp_ru_stime, &u.ru_stime); } /* refcount++ so that the timer is shut down only on request finish if not stopped manually */ zend_list_addref(t->rsrc_id); RETURN_RESOURCE(t->rsrc_id); } /* }}} */ /* {{{ proto resource pinba_timer_add(array tags, float value[, array data]) Create user timer with a value */ static PHP_FUNCTION(pinba_timer_add) { zval *tags_array, *data = NULL; pinba_timer_t *t = NULL; pinba_timer_tag_t **tags; int tags_num; double value; unsigned long time_l; if (PINBA_G(timers_stopped)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "all timers have already been stopped"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ad|a", &tags_array, &value, &data) != SUCCESS) { return; } tags_num = zend_hash_num_elements(Z_ARRVAL_P(tags_array)); if (!tags_num) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "tags array cannot be empty"); RETURN_FALSE; } if (php_pinba_array_to_tags(tags_array, &tags TSRMLS_CC) != SUCCESS) { RETURN_FALSE; } t = php_pinba_timer_ctor(tags, tags_num TSRMLS_CC); if (data) { MAKE_STD_ZVAL(t->data); *(t->data) = *data; zval_copy_ctor(t->data); INIT_PZVAL(t->data); } t->started = 0; t->hit_count = 1; time_l = (unsigned long)(value * 1000000.0); t->value.tv_sec = time_l / 1000000; t->value.tv_usec = time_l % 1000000; #if PHP_VERSION_ID >= 50400 t->rsrc_id = zend_list_insert(t, le_pinba_timer TSRMLS_CC); #else t->rsrc_id = zend_list_insert(t, le_pinba_timer); #endif /* refcount++ so that the timer is shut down only on request finish if not stopped manually */ zend_list_addref(t->rsrc_id); RETURN_RESOURCE(t->rsrc_id); } /* }}} */ /* {{{ proto bool pinba_timer_stop(resource timer) Stop user timer */ static PHP_FUNCTION(pinba_timer_stop) { zval *timer; pinba_timer_t *t; if (PINBA_G(timers_stopped)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "all timers have already been stopped"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &timer) != SUCCESS) { return; } PHP_ZVAL_TO_TIMER(timer, t); if (!t->started) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "timer is already stopped"); RETURN_FALSE; } php_pinba_timer_stop(t); RETURN_TRUE; } /* }}} */ /* {{{ proto bool pinba_timer_delete(resource timer) Delete user timer */ static PHP_FUNCTION(pinba_timer_delete) { zval *timer; pinba_timer_t *t; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &timer) != SUCCESS) { return; } PHP_ZVAL_TO_TIMER(timer, t); if (t->started) { php_pinba_timer_stop(t); } t->deleted = 1; zend_list_delete(t->rsrc_id); RETURN_TRUE; } /* }}} */ /* {{{ proto bool pinba_timer_data_merge(resource timer, array data) Merge timer data with new data */ static PHP_FUNCTION(pinba_timer_data_merge) { zval *timer, *data, *tmp; pinba_timer_t *t; if (PINBA_G(timers_stopped)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "all timers have already been stopped"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &timer, &data) != SUCCESS) { return; } PHP_ZVAL_TO_TIMER(timer, t); if (!t->data) { MAKE_STD_ZVAL(t->data); *(t->data) = *data; zval_copy_ctor(t->data); INIT_PZVAL(t->data); } else { zend_hash_merge(Z_ARRVAL_P(t->data), Z_ARRVAL_P(data), (void (*)(void *pData)) zval_add_ref, (void *) &tmp, sizeof(zval *), 1); } RETURN_TRUE; } /* }}} */ /* {{{ proto bool pinba_timer_data_replace(resource timer, array data) Replace timer data with new one */ static PHP_FUNCTION(pinba_timer_data_replace) { zval *timer, *data; pinba_timer_t *t; if (PINBA_G(timers_stopped)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "all timers have already been stopped"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra!", &timer, &data) != SUCCESS) { return; } PHP_ZVAL_TO_TIMER(timer, t); if (Z_TYPE_P(data) == IS_NULL) { /* reset */ if (t->data) { zval_ptr_dtor(&t->data); t->data = NULL; } } else { if (t->data) { zval_ptr_dtor(&t->data); t->data = NULL; } MAKE_STD_ZVAL(t->data); *(t->data) = *data; zval_copy_ctor(t->data); INIT_PZVAL(t->data); } RETURN_TRUE; } /* }}} */ /* {{{ proto bool pinba_timer_tags_merge(resource timer, array tags) Merge timer data with new data */ static PHP_FUNCTION(pinba_timer_tags_merge) { zval *timer, *tags; pinba_timer_t *t; pinba_timer_tag_t **new_tags; int i, j, tags_num; if (PINBA_G(timers_stopped)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "all timers have already been stopped"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &timer, &tags) != SUCCESS) { return; } PHP_ZVAL_TO_TIMER(timer, t); tags_num = zend_hash_num_elements(Z_ARRVAL_P(tags)); if (!tags_num) { RETURN_TRUE; } if (php_pinba_array_to_tags(tags, &new_tags TSRMLS_CC) != SUCCESS) { RETURN_FALSE; } for (i = 0; i < tags_num; i++) { int found = 0; for (j = 0; j < t->tags_num; j++) { if (t->tags[j]->name_len == new_tags[i]->name_len && memcmp(t->tags[j]->name, new_tags[i]->name, new_tags[i]->name_len) == 0) { found = 1; break; } } if (found == 1) { /* replace */ efree(t->tags[j]->value); t->tags[j]->value = estrndup(new_tags[i]->value, new_tags[i]->value_len); t->tags[j]->value_len = new_tags[i]->value_len; } else { /* add */ pinba_timer_tag_t *tag; t->tags = (pinba_timer_tag_t **)erealloc(t->tags, (t->tags_num + 1) * sizeof(pinba_timer_tag_t *)); tag = (pinba_timer_tag_t *)emalloc(sizeof(pinba_timer_tag_t)); tag->value = estrndup(new_tags[i]->value, new_tags[i]->value_len); tag->value_len = new_tags[i]->value_len; tag->name = estrndup(new_tags[i]->name, new_tags[i]->name_len); tag->name_len = new_tags[i]->name_len; t->tags[t->tags_num] = tag; t->tags_num++; } } php_pinba_timer_tags_dtor(new_tags, tags_num); efree(new_tags); RETURN_TRUE; } /* }}} */ /* {{{ proto bool pinba_timer_tags_replace(resource timer, array tags) Replace timer data with new one */ static PHP_FUNCTION(pinba_timer_tags_replace) { zval *timer, *tags; pinba_timer_t *t; pinba_timer_tag_t **new_tags; int tags_num; if (PINBA_G(timers_stopped)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "all timers have already been stopped"); RETURN_FALSE; } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ra", &timer, &tags) != SUCCESS) { return; } PHP_ZVAL_TO_TIMER(timer, t); tags_num = zend_hash_num_elements(Z_ARRVAL_P(tags)); if (!tags_num) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "tags array cannot be empty"); RETURN_TRUE; } if (php_pinba_array_to_tags(tags, &new_tags TSRMLS_CC) != SUCCESS) { RETURN_FALSE; } php_pinba_timer_tags_dtor(t->tags, t->tags_num); efree(t->tags); t->tags = new_tags; t->tags_num = tags_num; RETURN_TRUE; } /* }}} */ /* {{{ proto bool pinba_flush([string custom_script_name[, int flags]]) Flush the data */ static PHP_FUNCTION(pinba_flush) { zval **script_name = NULL; long flags = 0; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|Zl", &script_name, &flags) != SUCCESS) { return; } if (script_name && Z_TYPE_PP(script_name) != IS_NULL) { convert_to_string_ex(script_name); php_pinba_flush_data(Z_STRVAL_PP(script_name), flags TSRMLS_CC); } else { php_pinba_flush_data(NULL, flags TSRMLS_CC); } RETURN_TRUE; } /* }}} */ /* {{{ proto array pinba_get_info() Get request info */ static PHP_FUNCTION(pinba_get_info) { zval *timers, *timer_info; struct timeval tmp; struct rusage u; HashPosition pos; zend_rsrc_list_entry *le; pinba_timer_t *t; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") != SUCCESS) { return; } array_init(return_value); #if PHP_MAJOR_VERSION >= 5 add_assoc_long_ex(return_value, "mem_peak_usage", sizeof("mem_peak_usage"), zend_memory_peak_usage(1 TSRMLS_CC)); #elif PHP_MAJOR_VERSION == 4 && MEMORY_LIMIT add_assoc_long_ex(return_value, "mem_peak_usage", sizeof("mem_peak_usage"), AG(allocated_memory_peak)); #else add_assoc_long_ex(return_value, "mem_peak_usage", sizeof("mem_peak_usage"), 0); #endif if (gettimeofday(&tmp, 0) == 0) { timersub(&tmp, &(PINBA_G(tmp_req_data).req_start), &tmp); add_assoc_double_ex(return_value, "req_time", sizeof("req_time"), timeval_to_float(tmp)); } else { add_assoc_double_ex(return_value, "req_time", sizeof("req_time"), 0); } if (getrusage(RUSAGE_SELF, &u) == 0) { timersub(&u.ru_utime, &(PINBA_G(tmp_req_data).ru_utime), &tmp); add_assoc_double_ex(return_value, "ru_utime", sizeof("ru_utime"), timeval_to_float(tmp)); timersub(&u.ru_stime, &(PINBA_G(tmp_req_data).ru_stime), &tmp); add_assoc_double_ex(return_value, "ru_stime", sizeof("ru_stime"), timeval_to_float(tmp)); } else { add_assoc_double_ex(return_value, "ru_utime", sizeof("ru_utime"), 0); add_assoc_double_ex(return_value, "ru_stime", sizeof("ru_stime"), 0); } add_assoc_long_ex(return_value, "req_count", sizeof("req_count"), ++PINBA_G(tmp_req_data).req_count); add_assoc_long_ex(return_value, "doc_size", sizeof("doc_size"), PINBA_G(tmp_req_data).doc_size); if (PINBA_G(server_name)) { add_assoc_string_ex(return_value, "server_name", sizeof("server_name"), PINBA_G(server_name), 1); } else { add_assoc_string_ex(return_value, "server_name", sizeof("server_name"), (char *)"unknown", 1); } if (PINBA_G(script_name)) { add_assoc_string_ex(return_value, "script_name", sizeof("script_name"), PINBA_G(script_name), 1); } else { add_assoc_string_ex(return_value, "script_name", sizeof("script_name"), (char *)"unknown", 1); } MAKE_STD_ZVAL(timers); array_init(timers); for (zend_hash_internal_pointer_reset_ex(&EG(regular_list), &pos); zend_hash_get_current_data_ex((&EG(regular_list)), (void **) &le, &pos) == SUCCESS; zend_hash_move_forward_ex(&EG(regular_list), &pos)) { if (le->type == le_pinba_timer) { t = (pinba_timer_t *)le->ptr; if (t->deleted) { continue; } MAKE_STD_ZVAL(timer_info); php_pinba_get_timer_info(t, timer_info TSRMLS_CC); add_next_index_zval(timers, timer_info); } } add_assoc_zval_ex(return_value, "timers", sizeof("timers"), timers); } /* }}} */ /* {{{ proto array pinba_timer_get_info(resource timer) Get timer data */ static PHP_FUNCTION(pinba_timer_get_info) { zval *timer; pinba_timer_t *t; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &timer) != SUCCESS) { return; } PHP_ZVAL_TO_TIMER(timer, t); php_pinba_get_timer_info(t, return_value TSRMLS_CC); } /* }}} */ /* {{{ proto bool pinba_timers_stop() Stop all timers */ static PHP_FUNCTION(pinba_timers_stop) { zend_rsrc_list_entry *le; pinba_timer_t *t; HashPosition pos; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "") != SUCCESS) { return; } for (zend_hash_internal_pointer_reset_ex(&EG(regular_list), &pos); zend_hash_get_current_data_ex((&EG(regular_list)), (void **) &le, &pos) == SUCCESS; zend_hash_move_forward_ex(&EG(regular_list), &pos)) { if (le->type == le_pinba_timer) { t = (pinba_timer_t *)le->ptr; php_pinba_timer_stop(t); } } RETURN_TRUE; } /* }}} */ /* {{{ proto bool pinba_script_name_set(string custom_script_name) Set custom script name */ static PHP_FUNCTION(pinba_script_name_set) { char *script_name; int script_name_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &script_name, &script_name_len) != SUCCESS) { return; } if (PINBA_G(script_name)) { efree(PINBA_G(script_name)); } PINBA_G(script_name) = estrndup(script_name, script_name_len); RETURN_TRUE; } /* }}} */ /* {{{ proto bool pinba_hostname_set(string custom_hostname) Set custom hostname */ static PHP_FUNCTION(pinba_hostname_set) { char *hostname; int hostname_len; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &hostname, &hostname_len) != SUCCESS) { return; } if (hostname_len < sizeof(PINBA_G(host_name))) { memcpy(PINBA_G(host_name), hostname, hostname_len); PINBA_G(host_name)[hostname_len] = '\0'; } else { memcpy(PINBA_G(host_name), hostname, sizeof(PINBA_G(host_name)) - 1); PINBA_G(host_name)[sizeof(PINBA_G(host_name))] = '\0'; } RETURN_TRUE; } /* }}} */ /* {{{ pinba_functions[] */ zend_function_entry pinba_functions[] = { PHP_FE(pinba_timer_start, NULL) PHP_FE(pinba_timer_add, NULL) PHP_FE(pinba_timer_stop, NULL) PHP_FE(pinba_timer_delete, NULL) PHP_FE(pinba_timer_data_merge, NULL) PHP_FE(pinba_timer_data_replace, NULL) PHP_FE(pinba_timer_tags_merge, NULL) PHP_FE(pinba_timer_tags_replace, NULL) PHP_FE(pinba_flush, NULL) PHP_FE(pinba_get_info, NULL) PHP_FE(pinba_timer_get_info, NULL) PHP_FE(pinba_timers_stop, NULL) PHP_FE(pinba_script_name_set, NULL) PHP_FE(pinba_hostname_set, NULL) {NULL, NULL, NULL} }; /* }}} */ static PHP_INI_MH(OnUpdateCollectorAddress) /* {{{ */ { char *copy; char *new_node; char *new_service; if (new_value == NULL || new_value[0] == 0) { return FAILURE; } copy = strdup(new_value); if (copy == NULL) { return FAILURE; } new_node = NULL; new_service = NULL; /* '[' ']' [':' ] */ if (copy[0] == '[') { char *endptr; new_node = copy + 1; endptr = strchr(new_node, ']'); if (endptr == NULL) { free(copy); return FAILURE; } *endptr = 0; endptr++; if (*endptr != ':' && *endptr != 0) { free(copy); return FAILURE; } if (*endptr != 0) { new_service = endptr + 1; } if (new_service != NULL && *new_service == 0) { new_service = NULL; } } /* [':' ] */ else if ((strchr(copy, ':') == NULL) /* no colon */ || (strchr(copy, ':') == strrchr(copy, ':'))) { /* exactly one colon */ char *endptr = strchr(copy, ':'); if (endptr != NULL) { *endptr = 0; new_service = endptr + 1; } new_node = copy; } /* */ else { /* multiple colons */ new_node = copy; } if (PINBA_G(server_host)) { free(PINBA_G(server_host)); } if (PINBA_G(server_port)) { free(PINBA_G(server_port)); } PINBA_G(server_host) = strdup(new_node); if (new_service == NULL) { PINBA_G(server_port) = strdup(PINBA_COLLECTOR_DEFAULT_PORT); } else { PINBA_G(server_port) = strdup(new_service); } free(copy); /* Sets "collector_address", I assume */ return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); } /* }}} */ /* {{{ PHP_INI */ PHP_INI_BEGIN() STD_PHP_INI_ENTRY("pinba.server", NULL, PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateCollectorAddress, collector_address, zend_pinba_globals, pinba_globals) STD_PHP_INI_ENTRY("pinba.enabled", "0", PHP_INI_ALL, OnUpdateBool, enabled, zend_pinba_globals, pinba_globals) PHP_INI_END() /* }}} */ /* {{{ php_pinba_init_globals */ static void php_pinba_init_globals(zend_pinba_globals *globals) { memset(globals, 0, sizeof(*globals)); globals->timers_stopped = 0; globals->server_name = NULL; globals->script_name = NULL; } /* }}} */ /* {{{ PHP_MINIT_FUNCTION */ static PHP_MINIT_FUNCTION(pinba) { ZEND_INIT_MODULE_GLOBALS(pinba, php_pinba_init_globals, NULL); REGISTER_INI_ENTRIES(); le_pinba_timer = zend_register_list_destructors_ex(php_timer_resource_dtor, NULL, "pinba timer", module_number); REGISTER_LONG_CONSTANT("PINBA_FLUSH_ONLY_STOPPED_TIMERS", PINBA_FLUSH_ONLY_STOPPED_TIMERS, CONST_CS | CONST_PERSISTENT); return SUCCESS; } /* }}} */ /* {{{ PHP_MSHUTDOWN_FUNCTION */ static PHP_MSHUTDOWN_FUNCTION(pinba) { UNREGISTER_INI_ENTRIES(); if (pinba_socket > 0) { close(pinba_socket); } if (PINBA_G(server_host)) { free(PINBA_G(server_host)); } if (PINBA_G(server_port)) { free(PINBA_G(server_port)); } return SUCCESS; } /* }}} */ /* {{{ PHP_RINIT_FUNCTION */ static PHP_RINIT_FUNCTION(pinba) { zval **tmp; struct timeval t; struct rusage u; PINBA_G(timers_stopped) = 0; if (gettimeofday(&t, 0) == 0) { timeval_cvt(&(PINBA_G(tmp_req_data).req_start), &t); } else { return FAILURE; } if (getrusage(RUSAGE_SELF, &u) == 0) { timeval_cvt(&(PINBA_G(tmp_req_data).ru_utime), &u.ru_utime); timeval_cvt(&(PINBA_G(tmp_req_data).ru_stime), &u.ru_stime); } else { return FAILURE; } zend_hash_init(&PINBA_G(timers), 10, NULL, php_timer_hash_dtor, 0); PINBA_G(tmp_req_data).doc_size = 0; PINBA_G(tmp_req_data).mem_peak_usage= 0; PINBA_G(server_name) = NULL; PINBA_G(script_name) = NULL; gethostname(PINBA_G(host_name), sizeof(PINBA_G(host_name))); PINBA_G(host_name)[sizeof(PINBA_G(host_name))] = '\0'; #if PHP_MAJOR_VERSION >= 5 zend_is_auto_global("_SERVER", sizeof("_SERVER") - 1 TSRMLS_CC); #endif if (PG(http_globals)[TRACK_VARS_SERVER] && zend_hash_find(HASH_OF(PG(http_globals)[TRACK_VARS_SERVER]), "SCRIPT_NAME", sizeof("SCRIPT_NAME"), (void **) &tmp) != FAILURE && Z_TYPE_PP(tmp) == IS_STRING && Z_STRLEN_PP(tmp) > 0) { PINBA_G(script_name) = estrndup(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); } if (PG(http_globals)[TRACK_VARS_SERVER] && zend_hash_find(HASH_OF(PG(http_globals)[TRACK_VARS_SERVER]), "SERVER_NAME", sizeof("SERVER_NAME"), (void **) &tmp) != FAILURE && Z_TYPE_PP(tmp) == IS_STRING && Z_STRLEN_PP(tmp) > 0) { PINBA_G(server_name) = estrndup(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp)); } #if PHP_VERSION_ID < 50400 PINBA_G(old_sapi_ub_write) = OG(php_header_write); OG(php_header_write) = sapi_ub_write_counter; #else /* new output API */ PINBA_G(old_sapi_ub_write) = sapi_module.ub_write; sapi_module.ub_write = sapi_ub_write_counter; #endif return SUCCESS; } /* }}} */ /* {{{ PHP_RSHUTDOWN_FUNCTION */ static PHP_RSHUTDOWN_FUNCTION(pinba) { php_pinba_flush_data(NULL, 0 TSRMLS_CC); zend_hash_destroy(&PINBA_G(timers)); #if PHP_VERSION_ID < 50400 OG(php_header_write) = PINBA_G(old_sapi_ub_write); #else sapi_module.ub_write = PINBA_G(old_sapi_ub_write); #endif if (PINBA_G(server_name)) { efree(PINBA_G(server_name)); PINBA_G(server_name) = NULL; } if (PINBA_G(script_name)) { efree(PINBA_G(script_name)); PINBA_G(script_name) = NULL; } return SUCCESS; } /* }}} */ /* {{{ PHP_MINFO_FUNCTION */ static PHP_MINFO_FUNCTION(pinba) { string version; version = google::protobuf::internal::VersionString(GOOGLE_PROTOBUF_VERSION); php_info_print_table_start(); php_info_print_table_header(2, "Pinba support", "enabled"); php_info_print_table_row(2, "Extension version", PHP_PINBA_VERSION); php_info_print_table_row(2, "Google Protocol Buffers version", version.c_str()); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); } /* }}} */ /* {{{ pinba_module_entry */ zend_module_entry pinba_module_entry = { #if ZEND_MODULE_API_NO >= 20010901 STANDARD_MODULE_HEADER, #endif "pinba", pinba_functions, PHP_MINIT(pinba), PHP_MSHUTDOWN(pinba), PHP_RINIT(pinba), PHP_RSHUTDOWN(pinba), PHP_MINFO(pinba), #if ZEND_MODULE_API_NO >= 20010901 PHP_PINBA_VERSION, #endif STANDARD_MODULE_PROPERTIES }; /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: sw=4 ts=4 fdm=marker * vim<600: sw=4 ts=4 */ php-pinba-1.0.0/pinba-1.0.0/pinba.proto000066400000000000000000000012071213306657500172770ustar00rootroot00000000000000package Pinba; option optimize_for = SPEED; message Request { required string hostname = 1; required string server_name = 2; required string script_name = 3; required uint32 request_count = 4; required uint32 document_size = 5; required uint32 memory_peak = 6; required float request_time = 7; required float ru_utime = 8; required float ru_stime = 9; repeated uint32 timer_hit_count = 10; repeated float timer_value = 11; repeated uint32 timer_tag_count = 12; repeated uint32 timer_tag_name = 13; repeated uint32 timer_tag_value = 14; repeated string dictionary = 15; optional uint32 status = 16; }