root/mysqlnd/mysqlnd_bt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. mysqlnd_build_trace_args
  2. mysqlnd_build_trace_string
  3. mysqlnd_build_trace_args
  4. mysqlnd_build_trace_string
  5. mysqlnd_get_backtrace

/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 2006-2012 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.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: Georg Richter <georg@mysql.com>                             |
  |          Andrey Hristov <andrey@mysql.com>                           |
  |          Ulf Wendel <uwendel@mysql.com>                              |
  +----------------------------------------------------------------------+
*/

/* $Id: mysqlnd_debug.c 309303 2011-03-16 12:42:59Z andrey $ */

#include "php.h"
#include "Zend/zend_builtin_functions.h"

/* Follows code borrowed from zend_builtin_functions.c because the functions there are static */

#if MYSQLND_UNICODE
/* {{{ gettraceasstring() macros */
#define TRACE_APPEND_CHR(chr)                                            \
        *str = (char*)erealloc(*str, *len + 1 + 1);                          \
        (*str)[(*len)++] = chr

#define TRACE_APPEND_STRL(val, vallen)                                   \
        {                                                                    \
                int l = vallen;                                                  \
                *str = (char*)erealloc(*str, *len + l + 1);                      \
                memcpy((*str) + *len, val, l);                                   \
                *len += l;                                                       \
        }

#define TRACE_APPEND_USTRL(val, vallen) \
        { \
                zval tmp, copy; \
                int use_copy; \
                ZVAL_UNICODEL(&tmp, val, vallen, 1); \
                zend_make_printable_zval(&tmp, &copy, &use_copy); \
                TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
                zval_dtor(&copy); \
                zval_dtor(&tmp); \
        }

#define TRACE_APPEND_ZVAL(zv) \
        if (Z_TYPE_P((zv)) == IS_UNICODE) { \
                zval copy; \
                int use_copy; \
                zend_make_printable_zval((zv), &copy, &use_copy); \
                TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
                zval_dtor(&copy); \
        } else { \
                TRACE_APPEND_STRL(Z_STRVAL_P((zv)), Z_STRLEN_P((zv))); \
        }

#define TRACE_APPEND_STR(val)                                            \
        TRACE_APPEND_STRL(val, sizeof(val)-1)

#define TRACE_APPEND_KEY(key)                                            \
        if (zend_ascii_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
                if (Z_TYPE_PP(tmp) == IS_UNICODE) { \
                        zval copy; \
                        int use_copy; \
                        zend_make_printable_zval(*tmp, &copy, &use_copy); \
                        TRACE_APPEND_STRL(Z_STRVAL(copy), Z_STRLEN(copy)); \
                        zval_dtor(&copy); \
                } else { \
                TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \
                } \
        }
/* }}} */


/* {{{ mysqlnd_build_trace_args */
static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
{
        char **str;
        int *len;

        str = va_arg(args, char**);
        len = va_arg(args, int*);

        /* the trivial way would be to do:
         * conver_to_string_ex(arg);
         * append it and kill the now tmp arg.
         * but that could cause some E_NOTICE and also damn long lines.
         */

        switch (Z_TYPE_PP(arg)) {
                case IS_NULL:
                        TRACE_APPEND_STR("NULL, ");
                        break;
                case IS_STRING: {
                        int l_added;
                        TRACE_APPEND_CHR('\'');
                        if (Z_STRLEN_PP(arg) > 15) {
                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
                                TRACE_APPEND_STR("...', ");
                                l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
                        } else {
                                l_added = Z_STRLEN_PP(arg);
                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
                                TRACE_APPEND_STR("', ");
                                l_added += 3 + 1;
                        }
                        while (--l_added) {
                                if ((unsigned char)(*str)[*len - l_added] < 32) {
                                        (*str)[*len - l_added] = '?';
                                }
                        }
                        break;
                }
                case IS_UNICODE: {
                        int l_added;

                        /*
                         * We do not want to apply current error mode here, since
                         * zend_make_printable_zval() uses output encoding converter.
                         * Temporarily set output encoding converter to escape offending
                         * chars with \uXXXX notation.
                         */
                        zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, ZEND_CONV_ERROR_ESCAPE_JAVA);
                        TRACE_APPEND_CHR('\'');
                        if (Z_USTRLEN_PP(arg) > 15) {
                                TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), 15);
                                TRACE_APPEND_STR("...', ");
                                l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
                        } else {
                                l_added = Z_USTRLEN_PP(arg);
                                TRACE_APPEND_USTRL(Z_USTRVAL_PP(arg), l_added);
                                TRACE_APPEND_STR("', ");
                                l_added += 3 + 1;
                        }
                        /*
                         * Reset output encoding converter error mode.
                         */
                        zend_set_converter_error_mode(ZEND_U_CONVERTER(UG(output_encoding_conv)), ZEND_FROM_UNICODE, UG(from_error_mode));
                        while (--l_added) {
                                if ((unsigned char)(*str)[*len - l_added] < 32) {
                                        (*str)[*len - l_added] = '?';
                                }
                        }
                        break;
                }
                case IS_BOOL:
                        if (Z_LVAL_PP(arg)) {
                                TRACE_APPEND_STR("true, ");
                        } else {
                                TRACE_APPEND_STR("false, ");
                        }
                        break;
                case IS_RESOURCE:
                        TRACE_APPEND_STR("Resource id #");
                        /* break; */
                case IS_LONG: {
                        long lval = Z_LVAL_PP(arg);
                        char s_tmp[MAX_LENGTH_OF_LONG + 1];
                        int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                        TRACE_APPEND_STR(", ");
                        break;
                }
                case IS_DOUBLE: {
                        double dval = Z_DVAL_PP(arg);
                        char *s_tmp;
                        int l_tmp;

                        s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
                        l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                        /* %G already handles removing trailing zeros from the fractional part, yay */
                        efree(s_tmp);
                        TRACE_APPEND_STR(", ");
                        break;
                }
                case IS_ARRAY:
                        TRACE_APPEND_STR("Array, ");
                        break;
                case IS_OBJECT: {
                        zval tmp;
                        zstr class_name;
                        zend_uint class_name_len;
                        int dup;

                        TRACE_APPEND_STR("Object(");

                        dup = zend_get_object_classname(*arg, &class_name, &class_name_len TSRMLS_CC);

                        ZVAL_UNICODEL(&tmp, class_name.u, class_name_len, 1);
                        convert_to_string_with_converter(&tmp, ZEND_U_CONVERTER(UG(output_encoding_conv)));
                        TRACE_APPEND_STRL(Z_STRVAL(tmp), Z_STRLEN(tmp));
                        zval_dtor(&tmp);

                        if(!dup) {
                                efree(class_name.v);
                        }

                        TRACE_APPEND_STR("), ");
                        break;
                }
                default:
                        break;
        }
        return ZEND_HASH_APPLY_KEEP;
}
/* }}} */


static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
{
        char *s_tmp, **str;
        int *len, *num;
        long line;
        HashTable *ht = Z_ARRVAL_PP(frame);
        zval **file, **tmp;
        uint * level;

        level = va_arg(args, uint *);
        str = va_arg(args, char**);
        len = va_arg(args, int*);
        num = va_arg(args, int*);

        if (!*level) {
                return ZEND_HASH_APPLY_KEEP;
        }
        --*level;

        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
        sprintf(s_tmp, "#%d ", (*num)++);
        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
        efree(s_tmp);
        if (zend_ascii_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
                if (zend_ascii_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
                        line = Z_LVAL_PP(tmp);
                } else {
                        line = 0;
                }
                TRACE_APPEND_ZVAL(*file);
                s_tmp = emalloc(MAX_LENGTH_OF_LONG + 2 + 1);
                sprintf(s_tmp, "(%ld): ", line);
                TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                efree(s_tmp);
        } else {
                TRACE_APPEND_STR("[internal function]: ");
        }
        TRACE_APPEND_KEY("class");
        TRACE_APPEND_KEY("type");
        TRACE_APPEND_KEY("function");
        TRACE_APPEND_CHR('(');
        if (zend_ascii_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
                int last_len = *len;
                zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
                if (last_len != *len) {
                        *len -= 2; /* remove last ', ' */
                }
        }
        TRACE_APPEND_STR(")\n");
        return ZEND_HASH_APPLY_KEEP;
}
/* }}} */


#else /* PHP 5*/


/* {{{ gettraceasstring() macros */
#define TRACE_APPEND_CHR(chr)                                            \
        *str = (char*)erealloc(*str, *len + 1 + 1);                          \
        (*str)[(*len)++] = chr

#define TRACE_APPEND_STRL(val, vallen)                                   \
        {                                                                    \
                int l = vallen;                                                  \
                *str = (char*)erealloc(*str, *len + l + 1);                      \
                memcpy((*str) + *len, val, l);                                   \
                *len += l;                                                       \
        }

#define TRACE_APPEND_STR(val)                                            \
        TRACE_APPEND_STRL(val, sizeof(val)-1)

#define TRACE_APPEND_KEY(key)                                            \
        if (zend_hash_find(ht, key, sizeof(key), (void**)&tmp) == SUCCESS) { \
            TRACE_APPEND_STRL(Z_STRVAL_PP(tmp), Z_STRLEN_PP(tmp));           \
        }

/* }}} */


static int mysqlnd_build_trace_args(zval **arg TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
{
        char **str;
        int *len;

        str = va_arg(args, char**);
        len = va_arg(args, int*);

        /* the trivial way would be to do:
         * conver_to_string_ex(arg);
         * append it and kill the now tmp arg.
         * but that could cause some E_NOTICE and also damn long lines.
         */

        switch (Z_TYPE_PP(arg)) {
                case IS_NULL:
                        TRACE_APPEND_STR("NULL, ");
                        break;
                case IS_STRING: {
                        int l_added;
                        TRACE_APPEND_CHR('\'');
                        if (Z_STRLEN_PP(arg) > 15) {
                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), 15);
                                TRACE_APPEND_STR("...', ");
                                l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */
                        } else {
                                l_added = Z_STRLEN_PP(arg);
                                TRACE_APPEND_STRL(Z_STRVAL_PP(arg), l_added);
                                TRACE_APPEND_STR("', ");
                                l_added += 3 + 1;
                        }
                        while (--l_added) {
                                if ((*str)[*len - l_added] < 32) {
                                        (*str)[*len - l_added] = '?';
                                }
                        }
                        break;
                }
                case IS_BOOL:
                        if (Z_LVAL_PP(arg)) {
                                TRACE_APPEND_STR("true, ");
                        } else {
                                TRACE_APPEND_STR("false, ");
                        }
                        break;
                case IS_RESOURCE:
                        TRACE_APPEND_STR("Resource id #");
                        /* break; */
                case IS_LONG: {
                        long lval = Z_LVAL_PP(arg);
                        char s_tmp[MAX_LENGTH_OF_LONG + 1];
                        int l_tmp = zend_sprintf(s_tmp, "%ld", lval);  /* SAFE */
                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                        TRACE_APPEND_STR(", ");
                        break;
                }
                case IS_DOUBLE: {
                        double dval = Z_DVAL_PP(arg);
                        char *s_tmp;
                        int l_tmp;

                        s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1);
                        l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval);  /* SAFE */
                        TRACE_APPEND_STRL(s_tmp, l_tmp);
                        /* %G already handles removing trailing zeros from the fractional part, yay */
                        efree(s_tmp);
                        TRACE_APPEND_STR(", ");
                        break;
                }
                case IS_ARRAY:
                        TRACE_APPEND_STR("Array, ");
                        break;
                case IS_OBJECT: {
                        char *class_name;
                        zend_uint class_name_len;
                        int dupl;

                        TRACE_APPEND_STR("Object(");

                        dupl = zend_get_object_classname(*arg, (const char **)&class_name, &class_name_len TSRMLS_CC);

                        TRACE_APPEND_STRL(class_name, class_name_len);
                        if (!dupl) {
                                efree(class_name);
                        }

                        TRACE_APPEND_STR("), ");
                        break;
                }
                default:
                        break;
        }
        return ZEND_HASH_APPLY_KEEP;
}
/* }}} */

static int mysqlnd_build_trace_string(zval **frame TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key) /* {{{ */
{
        char *s_tmp, **str;
        int *len, *num;
        long line;
        HashTable *ht = Z_ARRVAL_PP(frame);
        zval **file, **tmp;
        uint * level;

        level = va_arg(args, uint *);
        str = va_arg(args, char**);
        len = va_arg(args, int*);
        num = va_arg(args, int*);

        if (!*level) {
                return ZEND_HASH_APPLY_KEEP;
        }
        --*level;

        s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1);
        sprintf(s_tmp, "#%d ", (*num)++);
        TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
        efree(s_tmp);
        if (zend_hash_find(ht, "file", sizeof("file"), (void**)&file) == SUCCESS) {
                if (zend_hash_find(ht, "line", sizeof("line"), (void**)&tmp) == SUCCESS) {
                        line = Z_LVAL_PP(tmp);
                } else {
                        line = 0;
                }
                s_tmp = emalloc(Z_STRLEN_PP(file) + MAX_LENGTH_OF_LONG + 4 + 1);
                sprintf(s_tmp, "%s(%ld): ", Z_STRVAL_PP(file), line);
                TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                efree(s_tmp);
        } else {
                TRACE_APPEND_STR("[internal function]: ");
        }
        TRACE_APPEND_KEY("class");
        TRACE_APPEND_KEY("type");
        TRACE_APPEND_KEY("function");
        TRACE_APPEND_CHR('(');
        if (zend_hash_find(ht, "args", sizeof("args"), (void**)&tmp) == SUCCESS) {
                int last_len = *len;
                zend_hash_apply_with_arguments(Z_ARRVAL_PP(tmp) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_args, 2, str, len);
                if (last_len != *len) {
                        *len -= 2; /* remove last ', ' */
                }
        }
        TRACE_APPEND_STR(")\n");
        return ZEND_HASH_APPLY_KEEP;
}
/* }}} */
#endif


PHPAPI char * mysqlnd_get_backtrace(uint max_levels, size_t * length TSRMLS_DC)
{
        zval *trace;
        char *res = estrdup(""), **str = &res, *s_tmp;
        int res_len = 0, *len = &res_len, num = 0;
        if (max_levels == 0) {
                max_levels = 99999;
        }

        MAKE_STD_ZVAL(trace);
        zend_fetch_debug_backtrace(trace, 0, 0, 0 TSRMLS_CC);

        zend_hash_apply_with_arguments(Z_ARRVAL_P(trace) TSRMLS_CC, (apply_func_args_t)mysqlnd_build_trace_string, 4, &max_levels, str, len, &num);
        zval_ptr_dtor(&trace);

        if (max_levels) {
                s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 7 + 1);
                sprintf(s_tmp, "#%d {main}", num);
                TRACE_APPEND_STRL(s_tmp, strlen(s_tmp));
                efree(s_tmp);
        }

        res[res_len] = '\0';
        *length = res_len;

        return res;
}


/*
 * 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
 */

/* [<][>][^][v][top][bottom][index][help] */