From e0df8cc8100f31534718e69cc41981e6c7490b20 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 21 Sep 2014 00:21:41 +0200 Subject: [PATCH] Use smart_str for exception stack traces --- Zend/zend_exceptions.c | 279 +++++++++++++++++------------------------ 1 file changed, 113 insertions(+), 166 deletions(-) diff --git a/Zend/zend_exceptions.c b/Zend/zend_exceptions.c index 9a270c0c10..8d3145ca23 100644 --- a/Zend/zend_exceptions.c +++ b/Zend/zend_exceptions.c @@ -28,6 +28,7 @@ #include "zend_exceptions.h" #include "zend_vm.h" #include "zend_dtrace.h" +#include "zend_smart_str.h" static zend_class_entry *default_exception_ce; static zend_class_entry *error_exception_ce; @@ -334,45 +335,78 @@ ZEND_METHOD(error_exception, getSeverity) } /* }}} */ -/* {{{ gettraceasstring() macros */ -#define TRACE_APPEND_CHR(chr) \ - str = zend_string_realloc(str, str->len + 1, 0); \ - str->val[str->len - 1] = chr - -#define TRACE_APPEND_STRL(v, l) \ - { \ - str = zend_string_realloc(str, str->len + (l), 0); \ - memcpy(str->val + str->len - (l), (v), (l)); \ - } - -#define TRACE_APPEND_STR(v) \ - TRACE_APPEND_STRL((v), sizeof((v))-1) - #define TRACE_APPEND_KEY(key) do { \ tmp = zend_hash_str_find(ht, key, sizeof(key)-1); \ if (tmp) { \ if (Z_TYPE_P(tmp) != IS_STRING) { \ zend_error(E_WARNING, "Value for %s is no string", key); \ - TRACE_APPEND_STR("[unknown]"); \ + smart_str_appends(str, "[unknown]"); \ } else { \ - TRACE_APPEND_STRL(Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); \ + smart_str_appendl(str, Z_STRVAL_P(tmp), Z_STRLEN_P(tmp)); \ } \ } \ } while (0) +/* Windows uses VK_ESCAPE instead of \e */ +#ifndef VK_ESCAPE +#define VK_ESCAPE '\e' +#endif -#define TRACE_ARG_APPEND(vallen) do { \ - size_t len = str->len; \ - str = zend_string_realloc(str, len + vallen, 0); \ - memmove(str->val + len - l_added + 1 + vallen, str->val + len - l_added + 1, l_added); \ - } while (0) +static size_t compute_escaped_string_len(const char *s, size_t l) { + size_t i, len = l; + for (i = 0; i < l; ++i) { + char c = s[i]; + if (c == '\n' || c == '\r' || c == '\t' || + c == '\f' || c == '\v' || c == '\\' || c == VK_ESCAPE) { + len += 1; + } else if (c < 32 || c > 126) { + len += 3; + } + } + return len; +} -/* }}} */ +static void smart_str_append_escaped(smart_str *str, const char *s, size_t l) { + char *res; + size_t i, len = compute_escaped_string_len(s, l); + + smart_str_alloc(str, len, 0); + res = &str->s->val[str->s->len]; + str->s->len += len; + + for (i = 0; i < l; ++i) { + char c = s[i]; + if (c < 32 || c == '\\' || c > 126) { + *res++ = '\\'; + switch (c) { + case '\n': *res++ = 'n'; break; + case '\r': *res++ = 'r'; break; + case '\t': *res++ = 't'; break; + case '\f': *res++ = 'f'; break; + case '\v': *res++ = 'v'; break; + case '\\': *res++ = '\\'; break; + case VK_ESCAPE: *res++ = 'e'; break; + default: + *res++ = 'x'; + if ((c >> 4) < 10) { + *res++ = (c >> 4) + '0'; + } else { + *res++ = (c >> 4) + 'A' - 10; + } + if ((c & 0xf) < 10) { + *res++ = (c & 0xf) + '0'; + } else { + *res++ = (c & 0xf) + 'A' - 10; + } + } + } else { + *res++ = c; + } + } +} -static void _build_trace_args(zval *arg, zend_string **str_ptr TSRMLS_DC) /* {{{ */ +static void _build_trace_args(zval *arg, smart_str *str TSRMLS_DC) /* {{{ */ { - zend_string *str = *str_ptr; - /* the trivial way would be to do: * convert_to_string_ex(arg); * append it and kill the now tmp arg. @@ -382,159 +416,71 @@ static void _build_trace_args(zval *arg, zend_string **str_ptr TSRMLS_DC) /* {{{ ZVAL_DEREF(arg); switch (Z_TYPE_P(arg)) { case IS_NULL: - TRACE_APPEND_STR("NULL, "); + smart_str_appends(str, "NULL, "); break; - case IS_STRING: { - int l_added; - TRACE_APPEND_CHR('\''); + case IS_STRING: + smart_str_appendc(str, '\''); + smart_str_append_escaped(str, Z_STRVAL_P(arg), MIN(Z_STRLEN_P(arg), 15)); if (Z_STRLEN_P(arg) > 15) { - TRACE_APPEND_STRL(Z_STRVAL_P(arg), 15); - TRACE_APPEND_STR("...', "); - l_added = 15 + 6 + 1; /* +1 because of while (--l_added) */ + smart_str_appends(str, "...', "); } else { - l_added = Z_STRLEN_P(arg); - TRACE_APPEND_STRL(Z_STRVAL_P(arg), l_added); - TRACE_APPEND_STR("', "); - l_added += 3 + 1; - } - while (--l_added) { - unsigned char chr = str->val[str->len - l_added]; - if (chr < 32 || chr == '\\' || chr > 126) { - str->val[str->len - l_added] = '\\'; - - switch (chr) { - case '\n': - TRACE_ARG_APPEND(1); - str->val[str->len - l_added] = 'n'; - break; - case '\r': - TRACE_ARG_APPEND(1); - str->val[str->len - l_added] = 'r'; - break; - case '\t': - TRACE_ARG_APPEND(1); - str->val[str->len - l_added] = 't'; - break; - case '\f': - TRACE_ARG_APPEND(1); - str->val[str->len - l_added] = 'f'; - break; - case '\v': - TRACE_ARG_APPEND(1); - str->val[str->len - l_added] = 'v'; - break; -#ifndef PHP_WIN32 - case '\e': -#else - case VK_ESCAPE: -#endif - TRACE_ARG_APPEND(1); - str->val[str->len - l_added] = 'e'; - break; - case '\\': - TRACE_ARG_APPEND(1); - str->val[str->len - l_added] = '\\'; - break; - default: - TRACE_ARG_APPEND(3); - str->val[str->len - l_added - 2] = 'x'; - if ((chr >> 4) < 10) { - str->val[str->len - l_added - 1] = (chr >> 4) + '0'; - } else { - str->val[str->len - l_added - 1] = (chr >> 4) + 'A' - 10; - } - if (chr % 16 < 10) { - str->val[str->len - l_added] = chr % 16 + '0'; - } else { - str->val[str->len - l_added] = chr % 16 + 'A' - 10; - } - } - } + smart_str_appends(str, "', "); } break; - } case IS_FALSE: - TRACE_APPEND_STR("false, "); + smart_str_appends(str, "false, "); break; case IS_TRUE: - TRACE_APPEND_STR("true, "); + smart_str_appends(str, "true, "); break; - case IS_RESOURCE: { - zend_long lval = Z_RES_HANDLE_P(arg); - char s_tmp[MAX_LENGTH_OF_LONG + 1]; - int l_tmp = zend_sprintf(s_tmp, ZEND_LONG_FMT, lval); /* SAFE */ - TRACE_APPEND_STR("Resource id #"); - TRACE_APPEND_STRL(s_tmp, l_tmp); - TRACE_APPEND_STR(", "); + case IS_RESOURCE: + smart_str_appends(str, "Resource id #"); + smart_str_append_long(str, Z_RES_HANDLE_P(arg)); + smart_str_appends(str, ", "); break; - } - case IS_LONG: { - zend_long lval = Z_LVAL_P(arg); - char s_tmp[MAX_LENGTH_OF_LONG + 1]; - int l_tmp = zend_sprintf(s_tmp, ZEND_LONG_FMT, lval); /* SAFE */ - TRACE_APPEND_STRL(s_tmp, l_tmp); - TRACE_APPEND_STR(", "); + case IS_LONG: + smart_str_append_long(str, Z_LVAL_P(arg)); + smart_str_appends(str, ", "); break; - } case IS_DOUBLE: { double dval = Z_DVAL_P(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 */ + char *s_tmp = emalloc(MAX_LENGTH_OF_DOUBLE + EG(precision) + 1); + int l_tmp = zend_sprintf(s_tmp, "%.*G", (int) EG(precision), dval); /* SAFE */ + smart_str_appendl(str, s_tmp, l_tmp); + smart_str_appends(str, ", "); efree(s_tmp); - TRACE_APPEND_STR(", "); break; } case IS_ARRAY: - TRACE_APPEND_STR("Array, "); + smart_str_appends(str, "Array, "); break; case IS_OBJECT: { - zend_string *class_name; - - TRACE_APPEND_STR("Object("); - - class_name = zend_get_object_classname(Z_OBJ_P(arg) TSRMLS_CC); + zend_string *class_name = zend_get_object_classname(Z_OBJ_P(arg) TSRMLS_CC); - TRACE_APPEND_STRL(class_name->val, class_name->len); - TRACE_APPEND_STR("), "); + smart_str_appends(str, "Object("); + smart_str_appendl(str, class_name->val, class_name->len); + smart_str_appends(str, "), "); break; } - default: - break; } - *str_ptr = str; } /* }}} */ -static void _build_trace_string(zval *frame, zend_ulong index, zend_string **str_ptr, int *num TSRMLS_DC) /* {{{ */ +static void _build_trace_string(smart_str *str, HashTable *ht, uint32_t num TSRMLS_DC) /* {{{ */ { - char *s_tmp; - int len; - zend_long line; - HashTable *ht; zval *file, *tmp; - zend_string *str = *str_ptr; - if (Z_TYPE_P(frame) != IS_ARRAY) { - zend_error(E_WARNING, "Expected array for frame %pu", index); - return; - } + smart_str_appendc(str, '#'); + smart_str_append_long(str, num); + smart_str_appendc(str, ' '); - ht = Z_ARRVAL_P(frame); - s_tmp = emalloc(1 + MAX_LENGTH_OF_LONG + 1 + 1); - len = sprintf(s_tmp, "#%d ", (*num)++); - TRACE_APPEND_STRL(s_tmp, len); - efree(s_tmp); file = zend_hash_str_find(ht, "file", sizeof("file")-1); if (file) { if (Z_TYPE_P(file) != IS_STRING) { zend_error(E_WARNING, "Function name is no string"); - TRACE_APPEND_STR("[unknown function]"); + smart_str_appends(str, "[unknown function]"); } else{ + zend_long line; tmp = zend_hash_str_find(ht, "line", sizeof("line")-1); if (tmp) { if (Z_TYPE_P(tmp) == IS_LONG) { @@ -546,37 +492,36 @@ static void _build_trace_string(zval *frame, zend_ulong index, zend_string **str } else { line = 0; } - s_tmp = emalloc(Z_STRLEN_P(file) + MAX_LENGTH_OF_LONG + 4 + 1); - len = sprintf(s_tmp, "%s(" ZEND_LONG_FMT "): ", Z_STRVAL_P(file), line); - TRACE_APPEND_STRL(s_tmp, len); - efree(s_tmp); + smart_str_appendl(str, Z_STRVAL_P(file), Z_STRLEN_P(file)); + smart_str_appendc(str, '('); + smart_str_append_long(str, line); + smart_str_appends(str, "): "); } } else { - TRACE_APPEND_STR("[internal function]: "); + smart_str_appends(str, "[internal function]: "); } TRACE_APPEND_KEY("class"); TRACE_APPEND_KEY("type"); TRACE_APPEND_KEY("function"); - TRACE_APPEND_CHR('('); + smart_str_appendc(str, '('); tmp = zend_hash_str_find(ht, "args", sizeof("args")-1); if (tmp) { if (Z_TYPE_P(tmp) == IS_ARRAY) { - int last_len = str->len; + size_t last_len = str->s->len; zval *arg; ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(tmp), arg) { - _build_trace_args(arg, &str TSRMLS_CC); + _build_trace_args(arg, str TSRMLS_CC); } ZEND_HASH_FOREACH_END(); - if (last_len != str->len) { - str->len -= 2; /* remove last ', ' */ + if (last_len != str->s->len) { + str->s->len -= 2; /* remove last ', ' */ } } else { zend_error(E_WARNING, "args element is no array"); } } - TRACE_APPEND_STR(")\n"); - *str_ptr = str; + smart_str_appends(str, ")\n"); } /* }}} */ @@ -586,25 +531,27 @@ ZEND_METHOD(exception, getTraceAsString) { zval *trace, *frame; zend_ulong index; - zend_string *str; - int num = 0, len; - char s_tmp[MAX_LENGTH_OF_LONG + 7 + 1 + 1]; + smart_str str = {0}; + uint32_t num = 0; DEFAULT_0_PARAMS; - str = zend_string_alloc(0, 0); - trace = zend_read_property(default_exception_ce, getThis(), "trace", sizeof("trace")-1, 1 TSRMLS_CC); ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(trace), index, frame) { - _build_trace_string(frame, index, &str, &num TSRMLS_CC); - } ZEND_HASH_FOREACH_END(); + if (Z_TYPE_P(frame) != IS_ARRAY) { + zend_error(E_WARNING, "Expected array for frame %pu", index); + continue; + } - len = sprintf(s_tmp, "#%d {main}", num); - TRACE_APPEND_STRL(s_tmp, len); + _build_trace_string(&str, Z_ARRVAL_P(frame), num++ TSRMLS_CC); + } ZEND_HASH_FOREACH_END(); - str->val[str->len] = '\0'; + smart_str_appendc(&str, '#'); + smart_str_append_long(&str, num); + smart_str_appends(&str, " {main}"); + smart_str_0(&str); - RETURN_NEW_STR(str); + RETURN_NEW_STR(str.s); } /* }}} */ -- 2.40.0