Skip to content

Commit 0ada938

Browse files
authored
Zend: Preallocate error buffer with capacity tracking (#20565)
Replace separate num_errors/errors fields with a single struct containing size, capacity, and a flexible array. The buffer grows by 50% when needed instead of reallocating on every recorded error.
1 parent cd75300 commit 0ada938

File tree

4 files changed

+45
-48
lines changed

4 files changed

+45
-48
lines changed

Zend/zend.c

Lines changed: 26 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -833,8 +833,7 @@ static void executor_globals_ctor(zend_executor_globals *executor_globals) /* {{
833833
#endif
834834
executor_globals->flags = EG_FLAGS_INITIAL;
835835
executor_globals->record_errors = false;
836-
executor_globals->num_errors = 0;
837-
executor_globals->errors = NULL;
836+
memset(&executor_globals->errors, 0, sizeof(executor_globals->errors));
838837
executor_globals->filename_override = NULL;
839838
executor_globals->lineno_override = -1;
840839
#ifdef ZEND_CHECK_STACK_LIMIT
@@ -1446,8 +1445,7 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
14461445
zend_stack delayed_oplines_stack;
14471446
int type = orig_type & E_ALL;
14481447
bool orig_record_errors;
1449-
uint32_t orig_num_errors;
1450-
zend_error_info **orig_errors;
1448+
zend_err_buf orig_errors_buf;
14511449
zend_result res;
14521450

14531451
/* If we're executing a function during SCCP, count any warnings that may be emitted,
@@ -1459,11 +1457,9 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
14591457
}
14601458

14611459
/* Emit any delayed error before handling fatal error */
1462-
if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(num_errors)) {
1463-
uint32_t num_errors = EG(num_errors);
1464-
zend_error_info **errors = EG(errors);
1465-
EG(num_errors) = 0;
1466-
EG(errors) = NULL;
1460+
if ((type & E_FATAL_ERRORS) && !(type & E_DONT_BAIL) && EG(errors).size) {
1461+
zend_err_buf errors_buf = EG(errors);
1462+
EG(errors).size = 0;
14671463

14681464
bool orig_record_errors = EG(record_errors);
14691465
EG(record_errors) = false;
@@ -1473,12 +1469,11 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
14731469
int orig_user_error_handler_error_reporting = EG(user_error_handler_error_reporting);
14741470
EG(user_error_handler_error_reporting) = 0;
14751471

1476-
zend_emit_recorded_errors_ex(num_errors, errors);
1472+
zend_emit_recorded_errors_ex(errors_buf.size, errors_buf.errors);
14771473

14781474
EG(user_error_handler_error_reporting) = orig_user_error_handler_error_reporting;
14791475
EG(record_errors) = orig_record_errors;
1480-
EG(num_errors) = num_errors;
1481-
EG(errors) = errors;
1476+
EG(errors) = errors_buf;
14821477
}
14831478

14841479
if (EG(record_errors)) {
@@ -1487,12 +1482,13 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
14871482
info->lineno = error_lineno;
14881483
info->filename = zend_string_copy(error_filename);
14891484
info->message = zend_string_copy(message);
1490-
1491-
/* This is very inefficient for a large number of errors.
1492-
* Use pow2 realloc if it becomes a problem. */
1493-
EG(num_errors)++;
1494-
EG(errors) = erealloc(EG(errors), sizeof(zend_error_info*) * EG(num_errors));
1495-
EG(errors)[EG(num_errors)-1] = info;
1485+
EG(errors).size++;
1486+
if (EG(errors).size > EG(errors).capacity) {
1487+
uint32_t capacity = EG(errors).capacity ? EG(errors).capacity + (EG(errors).capacity >> 1) : 2;
1488+
EG(errors).errors = erealloc(EG(errors).errors, sizeof(zend_error_info *) * capacity);
1489+
EG(errors).capacity = capacity;
1490+
}
1491+
EG(errors).errors[EG(errors).size - 1] = info;
14961492

14971493
/* Do not process non-fatal recorded error */
14981494
if (!(type & E_FATAL_ERRORS) || (type & E_DONT_BAIL)) {
@@ -1575,17 +1571,15 @@ ZEND_API ZEND_COLD void zend_error_zstr_at(
15751571
}
15761572

15771573
orig_record_errors = EG(record_errors);
1578-
orig_num_errors = EG(num_errors);
1579-
orig_errors = EG(errors);
15801574
EG(record_errors) = false;
1581-
EG(num_errors) = 0;
1582-
EG(errors) = NULL;
1575+
1576+
orig_errors_buf = EG(errors);
1577+
memset(&EG(errors), 0, sizeof(EG(errors)));
15831578

15841579
res = call_user_function(CG(function_table), NULL, &orig_user_error_handler, &retval, 4, params);
15851580

15861581
EG(record_errors) = orig_record_errors;
1587-
EG(num_errors) = orig_num_errors;
1588-
EG(errors) = orig_errors;
1582+
EG(errors) = orig_errors_buf;
15891583

15901584
if (res == SUCCESS) {
15911585
if (Z_TYPE(retval) != IS_UNDEF) {
@@ -1780,8 +1774,7 @@ ZEND_API void zend_begin_record_errors(void)
17801774
{
17811775
ZEND_ASSERT(!EG(record_errors) && "Error recording already enabled");
17821776
EG(record_errors) = true;
1783-
EG(num_errors) = 0;
1784-
EG(errors) = NULL;
1777+
EG(errors).size = 0;
17851778
}
17861779

17871780
ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info **errors)
@@ -1795,24 +1788,23 @@ ZEND_API void zend_emit_recorded_errors_ex(uint32_t num_errors, zend_error_info
17951788
ZEND_API void zend_emit_recorded_errors(void)
17961789
{
17971790
EG(record_errors) = false;
1798-
zend_emit_recorded_errors_ex(EG(num_errors), EG(errors));
1791+
zend_emit_recorded_errors_ex(EG(errors).size, EG(errors).errors);
17991792
}
18001793

18011794
ZEND_API void zend_free_recorded_errors(void)
18021795
{
1803-
if (!EG(num_errors)) {
1796+
if (!EG(errors).size) {
18041797
return;
18051798
}
18061799

1807-
for (uint32_t i = 0; i < EG(num_errors); i++) {
1808-
zend_error_info *info = EG(errors)[i];
1800+
for (uint32_t i = 0; i < EG(errors).size; i++) {
1801+
zend_error_info *info = EG(errors).errors[i];
18091802
zend_string_release(info->filename);
18101803
zend_string_release(info->message);
1811-
efree(info);
1804+
efree_size(info, sizeof(zend_error_info));
18121805
}
1813-
efree(EG(errors));
1814-
EG(errors) = NULL;
1815-
EG(num_errors) = 0;
1806+
efree(EG(errors).errors);
1807+
memset(&EG(errors), 0, sizeof(EG(errors)));
18161808
}
18171809

18181810
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format, ...) /* {{{ */

Zend/zend_execute_API.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,7 @@ void init_executor(void) /* {{{ */
192192
EG(get_gc_buffer).start = EG(get_gc_buffer).end = EG(get_gc_buffer).cur = NULL;
193193

194194
EG(record_errors) = false;
195-
EG(num_errors) = 0;
196-
EG(errors) = NULL;
195+
memset(&EG(errors), 0, sizeof(EG(errors)));
197196

198197
EG(filename_override) = NULL;
199198
EG(lineno_override) = -1;

Zend/zend_globals.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,20 @@ typedef struct _zend_vm_stack *zend_vm_stack;
7474
typedef struct _zend_ini_entry zend_ini_entry;
7575
typedef struct _zend_fiber_context zend_fiber_context;
7676
typedef struct _zend_fiber zend_fiber;
77+
typedef struct _zend_error_info zend_error_info;
7778

7879
typedef enum {
7980
ZEND_MEMOIZE_NONE,
8081
ZEND_MEMOIZE_COMPILE,
8182
ZEND_MEMOIZE_FETCH,
8283
} zend_memoize_mode;
8384

85+
typedef struct zend_err_buf {
86+
uint32_t size;
87+
uint32_t capacity;
88+
zend_error_info **errors;
89+
} zend_err_buf;
90+
8491
struct _zend_compiler_globals {
8592
zend_stack loop_var_stack;
8693

@@ -298,8 +305,7 @@ struct _zend_executor_globals {
298305
* and their processing is delayed until zend_emit_recorded_errors()
299306
* is called or a fatal diagnostic is emitted. */
300307
bool record_errors;
301-
uint32_t num_errors;
302-
zend_error_info **errors;
308+
zend_err_buf errors;
303309

304310
/* Override filename or line number of thrown errors and exceptions */
305311
zend_string *filename_override;

ext/opcache/ZendAccelerator.c

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,8 +1968,8 @@ static zend_op_array *file_cache_compile_file(zend_file_handle *file_handle, int
19681968

19691969
if (persistent_script) {
19701970
if (ZCG(accel_directives).record_warnings) {
1971-
persistent_script->num_warnings = EG(num_errors);
1972-
persistent_script->warnings = EG(errors);
1971+
persistent_script->num_warnings = EG(errors).size;
1972+
persistent_script->warnings = EG(errors).errors;
19731973
}
19741974

19751975
from_memory = false;
@@ -2193,8 +2193,8 @@ zend_op_array *persistent_compile_file(zend_file_handle *file_handle, int type)
21932193
from_shared_memory = false;
21942194
if (persistent_script) {
21952195
if (ZCG(accel_directives).record_warnings) {
2196-
persistent_script->num_warnings = EG(num_errors);
2197-
persistent_script->warnings = EG(errors);
2196+
persistent_script->num_warnings = EG(errors).size;
2197+
persistent_script->warnings = EG(errors).errors;
21982198
}
21992199

22002200
/* See GH-17246: we disable GC so that user code cannot be executed during the optimizer run. */
@@ -2421,7 +2421,7 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce,
24212421
}
24222422
ZCG(current_persistent_script) = &dummy;
24232423
zend_persist_class_entry_calc(ce);
2424-
zend_persist_warnings_calc(EG(num_errors), EG(errors));
2424+
zend_persist_warnings_calc(EG(errors).size, EG(errors).errors);
24252425
size = dummy.size;
24262426

24272427
zend_shared_alloc_clear_xlat_table();
@@ -2491,8 +2491,8 @@ static zend_class_entry* zend_accel_inheritance_cache_add(zend_class_entry *ce,
24912491
JIT_G(on) = jit_on_old;
24922492
#endif
24932493

2494-
entry->num_warnings = EG(num_errors);
2495-
entry->warnings = zend_persist_warnings(EG(num_errors), EG(errors));
2494+
entry->num_warnings = EG(errors).size;
2495+
entry->warnings = zend_persist_warnings(EG(errors).size, EG(errors).errors);
24962496
entry->next = proto->inheritance_cache;
24972497
proto->inheritance_cache = entry;
24982498

@@ -4123,9 +4123,9 @@ static void preload_link(void)
41234123
/* Remember the last error. */
41244124
zend_error_cb = orig_error_cb;
41254125
EG(record_errors) = false;
4126-
ZEND_ASSERT(EG(num_errors) > 0);
4127-
zend_hash_update_ptr(&errors, key, EG(errors)[EG(num_errors)-1]);
4128-
EG(num_errors)--;
4126+
ZEND_ASSERT(EG(errors).size > 0);
4127+
zend_hash_update_ptr(&errors, key, EG(errors).errors[EG(errors).size-1]);
4128+
EG(errors).size--;
41294129
} zend_end_try();
41304130
CG(in_compilation) = false;
41314131
CG(compiled_filename) = NULL;

0 commit comments

Comments
 (0)