php-src/ext/mysqli/mysqli_profiler.c
Georg Richter 7cf921a56f profiler:
moved profiler stuff from php_mysql.h to separate include file
added port communication
added xml support for file and port output
2003-03-11 01:19:21 +00:00

494 lines
14 KiB
C

/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2003 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. |
+----------------------------------------------------------------------+
| Author: Georg Richter <georg@php.net> |
+----------------------------------------------------------------------+
$Id$
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_mysqli.h"
#include "mysqli_profiler_com.h"
/* {{{ PR_COMMON *php_mysqli_profiler_new_object(PR_COMMON *parent, unsigned int type, unsigned int settime) */
PR_COMMON *php_mysqli_profiler_new_object(PR_COMMON *parent, unsigned int type, unsigned int settime)
{
PR_COMMON *prnew, *child;
switch (type) {
case MYSQLI_PR_MYSQL:
prnew = (PR_COMMON *)ecalloc(1, sizeof(PR_MYSQL));
break;
case MYSQLI_PR_QUERY:
prnew = (PR_COMMON *)ecalloc(1, sizeof(PR_QUERY));
break;
case MYSQLI_PR_STMT:
prnew = (PR_COMMON *)ecalloc(1, sizeof(PR_STMT));
break;
case MYSQLI_PR_COMMAND:
prnew = (PR_COMMON *)ecalloc(1, sizeof(PR_COMMAND));
break;
case MYSQLI_PR_RESULT:
prnew = (PR_COMMON *)ecalloc(1, sizeof(PR_RESULT));
break;
}
prnew->header.type = type;
prnew->header.filename = estrdup(zend_get_executed_filename(TSRMLS_C));
prnew->header.lineno = zend_get_executed_lineno(TSRMLS_C);
prnew->header.functionname = estrdup(get_active_function_name(TSRMLS_C));
if (settime) {
gettimeofday(&prnew->header.starttime, NULL);
}
if (!parent) {
return prnew;
}
if (!parent->header.child) {
parent->header.child = (void *)prnew;
return (prnew);
}
child = parent->header.child;
while (child->header.next != NULL) {
child = child->header.next;
}
child->header.next = (void *)prnew;
return (prnew);
}
/* }}} */
/* {{{ int *php_mysqli_profiler_explain(PR_EXPLAIN *, PR_HEADER *, MYSQL *, char *) */
int php_mysqli_profiler_explain(PR_EXPLAIN *explain, PR_HEADER *header, MYSQL *mysql, char *query)
{
MYSQL_RES *res;
unsigned int i,j;
MYSQL_FIELD *fields;
MYSQL_ROW row;
char *newquery = (char *)emalloc(strlen(query) + 10);
sprintf (newquery, "EXPLAIN %s", query);
mysql_real_query(mysql, newquery, strlen(newquery));
efree (newquery);
if (mysql_errno(mysql)) {
header->error = mysql_errno(mysql);
header->errormsg = my_estrdup(mysql_error(mysql));
return 0;
}
res = mysql_store_result(mysql);
if (!(explain->exp_cnt = mysql_num_rows(res))) {
return 0;
}
explain->columns = mysql_num_fields(res);
explain->row = (PR_ROW *)emalloc(sizeof(PR_ROW) * explain->exp_cnt);
explain->fields = (char **)emalloc(sizeof(char *) * explain->columns);
fields = mysql_fetch_fields(res);
for (j=0; j < explain->columns; j++) {
explain->fields[j] = estrdup(fields[j].name);
}
for (i=0; i < explain->exp_cnt; i++) {
explain->row[i].value = (char **)emalloc(sizeof(char *) * explain->columns);
row = mysql_fetch_row(res);
for (j=0; j < explain->columns; j++) {
explain->row[i].value[j] = my_estrdup(row[j]);
}
}
mysql_free_result(res);
return 1;
}
/* }}} */
/* {{{ void php_mysqli_profiler_timediff(struct timeval, struct timeval *) */
void php_mysqli_profiler_timediff(struct timeval starttime, struct timeval *elapsed)
{
struct timeval end;
gettimeofday(&end, NULL);
elapsed->tv_sec = end.tv_sec - starttime.tv_sec;
elapsed->tv_usec = end.tv_usec - starttime.tv_usec;
if (elapsed->tv_usec < 0) {
--(elapsed->tv_sec);
elapsed->tv_usec = 1000000;
}
return;
}
/* }}} */
/* {{{ char *php_mysqli_profiler_indent(int) */
char *php_mysqli_profiler_indent(int i)
{
char *ret = (char *)ecalloc(i*4+1, sizeof(char));
memset(ret, 0x20, i*4);
return ret;
}
/* }}} */
/* {{{ void php_mysqli_profiler_report_header(PR_HEADER, char *, char *) */
void php_mysqli_profiler_report_header(PR_HEADER header, char *ident)
{
char buffer[8192];
switch (header.type) {
case MYSQLI_PR_MYSQL:
php_mysqli_profiler_timediff(header.starttime, &header.lifetime);
break;
case MYSQLI_PR_STMT:
php_mysqli_profiler_timediff(header.starttime, &header.lifetime);
break;
case MYSQLI_PR_RESULT:
php_mysqli_profiler_timediff(header.starttime, &header.lifetime);
break;
}
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "name", header.functionname);
MYSQLI_PROFILER_ADD_STARTTAG(buffer, "fileinfo",0);
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "name", header.filename);
MYSQLI_PROFILER_ADD_ATTR_LONG(buffer, "line", header.lineno);
MYSQLI_PROFILER_ADD_ENDTAG(buffer, "fileinfo");
MYSQLI_PROFILER_ADD_STARTTAG(buffer, "times",0);
MYSQLI_PROFILER_ADD_ATTR_TIME(buffer, "execution_time", header.elapsedtime.tv_sec, header.elapsedtime.tv_usec);
if (header.lifetime.tv_sec + header.lifetime.tv_usec) {
MYSQLI_PROFILER_ADD_ATTR_TIME(buffer, "life_time", header.lifetime.tv_sec, header.lifetime.tv_usec);
}
MYSQLI_PROFILER_ADD_ENDTAG(buffer, "times");
if (header.error) {
MYSQLI_PROFILER_ADD_STARTTAG(buffer, "errors",0);
MYSQLI_PROFILER_ADD_ATTR_LONG(buffer, "errno", header.error);
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "errmsg", header.errormsg);
MYSQLI_PROFILER_ADD_ENDTAG(buffer, "errors");
}
/* free header */
my_efree(header.functionname);
my_efree(header.filename);
my_efree(header.errormsg);
}
/* }}} */
/* {{{ void php_mysqli_profiler_report_explain(PR_EXPLAIN, char *, char *) */
void php_mysqli_profiler_report_explain(PR_EXPLAIN explain, char *ident)
{
int i, j;
char buffer[8192];
if (explain.query) {
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "query_string", explain.query);
}
if (explain.exp_cnt) {
MYSQLI_PROFILER_ADD_STARTTAG(buffer, "explain", 1);
for (i=0; i < explain.exp_cnt; i++) {
for (j=0; j < explain.columns; j++) {
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, explain.fields[j], explain.row[i].value[j]);
my_efree(explain.row[i].value[j]);
}
efree(explain.row[i].value);
}
MYSQLI_PROFILER_ADD_ENDTAG(buffer, "explain");
}
efree(explain.row);
for (j=0; j < explain.columns; j++) {
my_efree(explain.fields[j]);
}
efree(explain.fields);
/* free explain */
my_efree(explain.query);
}
/* }}} */
/* {{{ php_mysqli_profiler_report_mysql(PR_MYSQL *, int) */
void php_mysqli_profiler_report_mysql(PR_MYSQL *prmysql, int depth)
{
char *ident = php_mysqli_profiler_indent(depth);
char buffer[8192];
php_mysqli_profiler_report_header(prmysql->header, ident);
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "hostname", prmysql->hostname);
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "user", prmysql->username);
MYSQLI_PROFILER_ADD_ATTR_LONG(buffer, "thread_id", prmysql->thread_id);
if (!prmysql->closed) {
MYSQLI_PROFILER_ADD_STARTTAG(buffer, "warnings",0);
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "warning", "connection wasn't closed by mysqli_close()");
MYSQLI_PROFILER_ADD_ENDTAG(buffer, "warnings");
}
my_efree(prmysql->hostname);
my_efree(prmysql->username);
efree(ident);
}
/* }}} */
/* {{{ void php_mysqli_profiler_report_query(PR_QUERY *, int) */
void php_mysqli_profiler_report_query(PR_QUERY *prquery, int depth)
{
char buffer[8192];
char *ident = php_mysqli_profiler_indent(depth);
php_mysqli_profiler_report_header(prquery->header, ident);
php_mysqli_profiler_report_explain(prquery->explain, ident);
if (prquery->affectedrows > 0) {
MYSQLI_PROFILER_ADD_ATTR_LONG(buffer, "affected_rows", prquery->affectedrows);
}
if (prquery->insertid) {
MYSQLI_PROFILER_ADD_ATTR_LONG(buffer, "insert_id", prquery->insertid);
}
efree(ident);
}
/* }}} */
/* {{{ void php_mysqli_profiler_report_stmt(PR_STMT *, int) */
void php_mysqli_profiler_report_stmt(PR_STMT *prstmt, int depth)
{
char *ident = php_mysqli_profiler_indent(depth);
char buffer[8192];
buffer[0] = '\0';
php_mysqli_profiler_report_header(prstmt->header, ident);
php_mysqli_profiler_report_explain(prstmt->explain, ident);
printf("\n");
MYSQLI_PROFILER_OUT(buffer);
efree(ident);
}
/* }}} */
/* {{{ void php_mysqli_profiler_report_result(PR_RESULT *, int) */
void php_mysqli_profiler_report_result(PR_RESULT *prresult, int depth)
{
char *ident = php_mysqli_profiler_indent(depth);
char buffer[8192];
buffer[0] = '\0';
php_mysqli_profiler_report_header(prresult->header, ident);
MYSQLI_PROFILER_ADD_ATTR_LONG(buffer, "columns", prresult->columns);
MYSQLI_PROFILER_ADD_ATTR_LONG(buffer, "rows", prresult->rows);
MYSQLI_PROFILER_ADD_ATTR_LONG(buffer, "fetched_rows", prresult->fetched_rows);
if (!prresult->closed || prresult->fetched_rows != prresult->rows) {
MYSQLI_PROFILER_ADD_STARTTAG(buffer, "warnings",0);
if (!prresult->closed) {
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "warning", "resultset wasn't closed by mysqli_free_result()");
}
if (prresult->fetched_rows != prresult->rows) {
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "warning", "Not all rows from resultset were fetched.");
}
MYSQLI_PROFILER_ADD_ENDTAG(buffer, "warnings");
}
efree(ident);
}
/* }}} */
/* {{{ void php_mysqli_profiler_report_command(PR_COMMAND *, int) */
void php_mysqli_profiler_report_command(PR_COMMAND *prcommand, int depth)
{
char *ident = php_mysqli_profiler_indent(depth);
char buffer[8192];
buffer[0] = '\0';
php_mysqli_profiler_report_header(prcommand->header, ident);
if (prcommand->returnvalue) {
MYSQLI_PROFILER_ADD_ATTR_STRING(buffer, "return_value", prcommand->returnvalue);
efree(prcommand->returnvalue);
}
efree(ident);
}
/* }}} */
/* {{{ php_mysqli_profiler_report(PR_COMMON *, int) */
void php_mysqli_profiler_report(PR_COMMON *current, int depth)
{
PR_COMMON *child;
PR_COMMON *next;
char buffer[8192];
char tag[50];
char *ident = php_mysqli_profiler_indent(depth);
buffer[0] = '\0';
switch (current->header.type) {
case MYSQLI_PR_MAIN:
{
MYSQLI_PROFILER_ADD_STARTTAG(buffer,"?xml version=\"1.0\" ?", 0);
sprintf((char *)&tag, "profilerinfo source='PHP5' version='%2.1f'", MYSQLI_PROFILER_VERSION);
MYSQLI_PROFILER_ADD_STARTTAG(buffer, tag, 0);
strcpy (tag, "profilerinfo");
}
break;
case MYSQLI_PR_MYSQL:
{
PR_MYSQL *prmysql = (PR_MYSQL *)current;
strcpy(tag, "connection");
MYSQLI_PROFILER_ADD_STARTTAG(buffer, tag, 1);
php_mysqli_profiler_report_mysql(prmysql, depth);
}
break;
case MYSQLI_PR_COMMAND:
{
PR_COMMAND *prcommand = (PR_COMMAND *)current;
strcpy(tag, "command");
MYSQLI_PROFILER_ADD_STARTTAG(buffer, tag, 1);
php_mysqli_profiler_report_command(prcommand, depth);
}
break;
case MYSQLI_PR_RESULT:
{
PR_RESULT *prresult = (PR_RESULT *)current;
strcpy(tag, "resultset");
MYSQLI_PROFILER_ADD_STARTTAG(buffer, tag, 1);
php_mysqli_profiler_report_result(prresult, depth);
}
break;
case MYSQLI_PR_STMT:
case MYSQLI_PR_STMT_RESULT:
{
PR_STMT *prstmt = (PR_STMT *)current;
strcpy(tag, "statement");
MYSQLI_PROFILER_ADD_STARTTAG(buffer, tag, 1);
php_mysqli_profiler_report_stmt(prstmt, depth);
}
break;
case MYSQLI_PR_QUERY:
case MYSQLI_PR_QUERY_RESULT:
{
PR_QUERY *prquery = (PR_QUERY *)current;
strcpy(tag, "query");
MYSQLI_PROFILER_ADD_STARTTAG(buffer, tag, 1);
php_mysqli_profiler_report_query(prquery, depth);
}
break;
}
child = current->header.child;
if (child) {
php_mysqli_profiler_report(child, depth+1);
}
MYSQLI_PROFILER_ADD_ENDTAG(buffer, tag);
next = (current->header.next) ? current->header.next : NULL;
if (next) {
php_mysqli_profiler_report(next, depth);
}
if (current->header.type == MYSQLI_PR_MAIN) {
PR_MAIN *prmain = (PR_MAIN *)current;
my_efree(prmain->name);
}
efree(current);
efree(ident);
return;
}
/* }}} */
/* {{{ proto bool mysqli_profiler (int flags, char *info, int port)
*/
PHP_FUNCTION(mysqli_profiler)
{
int flag;
char *name;
int name_len = 0, port = 0;
int connection;
if (MyG(profiler)) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Profiler was already started.");
RETURN_FALSE;
}
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|sl", &flag, &name, &name_len, &port) == FAILURE) {
return;
}
switch (flag) {
case MYSQLI_PR_REPORT_STDERR:
prmain = ecalloc(1, sizeof(PR_MAIN));
prmain->mode = flag;
break;
case MYSQLI_PR_REPORT_PORT:
if (!name_len) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Hostname not specified.");
RETURN_FALSE;
}
if (!port) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Portnumber not specified.");
RETURN_FALSE;
}
if (!(connection = php_mysqli_create_socket(name, port))) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to connect to host %s on port %d, name, port");
RETURN_FALSE;
}
prmain = ecalloc(1, sizeof(PR_MAIN));
prmain->mode = flag;
prmain->port = port;
prmain->name = my_estrdup(name);
prmain->connection = connection;
break;
case MYSQLI_PR_REPORT_FILE:
if (!name_len) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Filename not specified.");
RETURN_FALSE;
}
prmain = ecalloc(1, sizeof(PR_MAIN));
if (!(prmain->fp = fopen(name, "w"))){
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't write to file %s.", name);
efree(prmain);
RETURN_FALSE;
}
prmain->mode = flag;
prmain->name = my_estrdup(name);
break;
default:
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported flag: %d", flag);
RETURN_FALSE;
}
/* PR_SSEND(port,"<application>");
php_mysqli_close_socket(prmain.connection);
*/
MyG(profiler) = flag;
RETURN_TRUE;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/