Browse Source

Open Source Release

pull/288/merge
Dmitry Stogov 13 years ago
commit
528006a3b4
  1. 87
      .gitignore
  2. 1967
      Optimizer/block_pass.c
  3. 126
      Optimizer/nop_removal.c
  4. 222
      Optimizer/optimize_temp_vars_5.c
  5. 3
      Optimizer/pass10.c
  6. 391
      Optimizer/pass1_5.c
  7. 210
      Optimizer/pass2.c
  8. 443
      Optimizer/pass3.c
  9. 3
      Optimizer/pass5.c
  10. 8
      Optimizer/pass9.c
  11. 139
      Optimizer/zend_optimizer.c
  12. 49
      Optimizer/zend_optimizer.h
  13. 76
      Optimizer/zend_optimizer_internal.h
  14. 171
      README
  15. 2510
      ZendAccelerator.c
  16. 364
      ZendAccelerator.h
  17. 345
      config.m4
  18. 28
      config.w32
  19. 70
      shared_alloc_mmap.c
  20. 90
      shared_alloc_posix.c
  21. 137
      shared_alloc_shm.c
  22. 315
      shared_alloc_win32.c
  23. 241
      zend_accelerator_blacklist.c
  24. 49
      zend_accelerator_blacklist.h
  25. 101
      zend_accelerator_debug.c
  26. 33
      zend_accelerator_debug.h
  27. 222
      zend_accelerator_hash.c
  28. 98
      zend_accelerator_hash.h
  29. 574
      zend_accelerator_module.c
  30. 28
      zend_accelerator_module.h
  31. 1079
      zend_accelerator_util_funcs.c
  32. 49
      zend_accelerator_util_funcs.h
  33. 680
      zend_persist.c
  34. 29
      zend_persist.h
  35. 343
      zend_persist_calc.c
  36. 473
      zend_shared_alloc.c
  37. 166
      zend_shared_alloc.h

87
.gitignore

@ -0,0 +1,87 @@
# General Ignores
*~
.#*
*.
*.slo
*.mk
*.mem
*.gcda
*.gcno
*.la
*.lo
*.o
*.a
*.ncb
*.opt
*.plg
*swp
*.patch
*.tgz
*.tar.gz
*.tar.bz2
.FBCIndex
.FBCLockFolder
.deps
.libs
phpt.*
core
dynlib.m4
Debug
Debug_TS
Makefile
Makefile.fragments
Makefile.objects
Makefile.global
Release
Release_TS
Release_TSDbg
Release_TS_inline
Release_inline
ZendEngine1
_libs
acconfig.h
aclocal.m4
acinclude.m4
autom4te.cache
bsd_converted
buildconf.stamp
buildmk.stamp
confdefs.h
config.h
config.guess
config.cache
config.h.in
config.log
config.nice
config.nice.bat
config.status
config.sub
config_vars.mk
configuration-parser.c
configuration-parser.h
configuration-parser.output
configuration-scanner.c
configure
configure.in
configure.bat
configure.js
conftest
conftest.c
debug.log
diff
generated_lists
include
install-sh
internal_functions.c
lcov_data
lcov_html
libs
libtool
ltmain.sh
meta_cc
meta_ccld
missing
mkinstalldirs
modules
build
run-tests.php

1967
Optimizer/block_pass.c
File diff suppressed because it is too large
View File

126
Optimizer/nop_removal.c

@ -0,0 +1,126 @@
/* pass 10:
* - remove NOPs
*/
static void nop_removal(zend_op_array *op_array)
{
zend_op *end, *opline;
zend_uint new_count, i, shift;
int j;
zend_uint *shiftlist;
ALLOCA_FLAG(use_heap);
shiftlist = (zend_uint *)DO_ALLOCA(sizeof(zend_uint) * op_array->last);
i = new_count = shift = 0;
end = op_array->opcodes+op_array->last;
for (opline = op_array->opcodes; opline < end; opline++) {
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
/* GOTO target is unresolved yet. We can't optimize. */
if (opline->opcode == ZEND_GOTO &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) != IS_LONG) {
/* TODO: in general we can avoid this restriction */
FREE_ALLOCA(shiftlist);
return;
}
#endif
/* Kill JMP-over-NOP-s */
if (opline->opcode == ZEND_JMP && ZEND_OP1(opline).opline_num > i) {
/* check if there are only NOPs under the branch */
zend_op *target = op_array->opcodes + ZEND_OP1(opline).opline_num - 1;
while (target->opcode == ZEND_NOP) {
target--;
}
if (target == opline) {
/* only NOPs */
opline->opcode = ZEND_NOP;
}
}
shiftlist[i++] = shift;
if (opline->opcode == ZEND_NOP) {
shift++;
} else {
if (shift) {
op_array->opcodes[new_count] = *opline;
}
new_count++;
}
}
if (shift) {
op_array->last = new_count;
end = op_array->opcodes + op_array->last;
/* update JMPs */
for (opline = op_array->opcodes; opline<end; opline++) {
switch (opline->opcode) {
case ZEND_JMP:
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
case ZEND_GOTO:
#endif
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
case ZEND_FAST_CALL:
#endif
ZEND_OP1(opline).opline_num -= shiftlist[ZEND_OP1(opline).opline_num];
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_FE_FETCH:
case ZEND_FE_RESET:
case ZEND_NEW:
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
case ZEND_JMP_SET:
#endif
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
case ZEND_JMP_SET_VAR:
#endif
ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num];
break;
case ZEND_JMPZNZ:
ZEND_OP2(opline).opline_num -= shiftlist[ZEND_OP2(opline).opline_num];
opline->extended_value -= shiftlist[opline->extended_value];
break;
case ZEND_CATCH:
opline->extended_value -= shiftlist[opline->extended_value];
break;
}
}
/* update brk/cont array */
for (i=0; i<op_array->last_brk_cont; i++) {
op_array->brk_cont_array[i].brk -= shiftlist[op_array->brk_cont_array[i].brk];
op_array->brk_cont_array[i].cont -= shiftlist[op_array->brk_cont_array[i].cont];
op_array->brk_cont_array[i].start -= shiftlist[op_array->brk_cont_array[i].start];
}
/* update try/catch array */
for (j=0; j<op_array->last_try_catch; j++) {
op_array->try_catch_array[j].try_op -= shiftlist[op_array->try_catch_array[j].try_op];
op_array->try_catch_array[j].catch_op -= shiftlist[op_array->try_catch_array[j].catch_op];
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
if (op_array->try_catch_array[j].finally_op) {
op_array->try_catch_array[j].finally_op -= shiftlist[op_array->try_catch_array[j].finally_op];
op_array->try_catch_array[j].finally_end -= shiftlist[op_array->try_catch_array[j].finally_end];
}
#endif
}
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
/* update early binding list */
if (op_array->early_binding != -1) {
zend_uint *opline_num = &op_array->early_binding;
do {
*opline_num -= shiftlist[*opline_num];
opline_num = &ZEND_RESULT(&op_array->opcodes[*opline_num]).opline_num;
} while (*opline_num != -1);
}
#endif
}
FREE_ALLOCA(shiftlist);
}

222
Optimizer/optimize_temp_vars_5.c

@ -0,0 +1,222 @@
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
/* ops that use CLs:
op1:
ZEND_FETCH_CONSTANT:
ZEND_INIT_CTOR_CALL:
ZEND_INIT_STATIC_METHOD_CALL:
ZEND_INIT_METHOD_CALL:
ZEND_IMPORT_CLASS:
ZEND_IMPORT_FUNCTION:
ZEND_IMPORT_CONST:
ZEND_ADD_INTERFACE:
ZEND_VERIFY_ABSTRACT_CLASS:
ZEND_NEW:
ZEND_CATCH:
ZEND_INIT_FCALL_BY_NAME:
op2:
ZEND_UNSET_VAR:
ZEND_ISSET_ISEMPTY_VAR:
ZEND_FETCH_UNSET:
ZEND_FETCH_IS:
ZEND_FETCH_R:
ZEND_FETCH_W:
ZEND_FETCH_RW:
ZEND_FETCH_FUNC_ARG:
ZEND_ADD_INTERFACE:
ZEND_INSTANCEOF:
extended_value:
ZEND_DECLARE_INHERITED_CLASS:
ignore result
INIT_METHOD_CALL:
*/
#define OP1_CONST_IS_CLASS 1
#define OP2_CONST_IS_CLASS 2
#define EXT_CONST_IS_CLASS 4
#define RESULT_IS_UNUSED 8
static const char op_const_means_class[256] = {
/* 0 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 32 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
/* 64 */
0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 2,
/* 96 */
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 9, 1, 2, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 128 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 4, 0, 0, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 160 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 192 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* 224 */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
#endif
#define GET_AVAILABLE_T() \
for (i=0; i<T; i++) { \
if (!taken_T[i]) { \
break; \
} \
} \
taken_T[i] = 1; \
if (i > max) { \
max = i; \
}
static void optimize_temporary_variables(zend_op_array *op_array)
{
int T = op_array->T;
char *taken_T; /* T index in use */
zend_op **start_of_T; /* opline where T is first used */
char *valid_T; /* Is the map_T valid */
int *map_T; /* Map's the T to its new index */
zend_op *opline, *end;
int currT;
int i;
int max = -1;
int var_to_free = -1;
taken_T = (char *) emalloc(T);
start_of_T = (zend_op **) emalloc(T*sizeof(zend_op *));
valid_T = (char *) emalloc(T);
map_T = (int *) emalloc(T*sizeof(int));
end = op_array->opcodes;
opline = &op_array->opcodes[op_array->last-1];
/* Find T definition points */
while (opline >= end) {
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) {
if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) {
start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline;
}
}
#else
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
start_of_T[VAR_NUM(ZEND_RESULT(opline).var)] = opline;
}
#endif
opline--;
}
memset(valid_T, 0, T);
memset(taken_T, 0, T);
end = op_array->opcodes;
opline = &op_array->opcodes[op_array->last-1];
while (opline >= end) {
if ((ZEND_OP1_TYPE(opline) & (IS_VAR | IS_TMP_VAR))
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
|| ((op_const_means_class[opline->opcode] & OP1_CONST_IS_CLASS) && ZEND_OP1_TYPE(opline) == IS_CONST)
#endif
) {
currT = VAR_NUM(ZEND_OP1(opline).var);
if (!valid_T[currT]) {
GET_AVAILABLE_T();
map_T[currT] = i;
valid_T[currT] = 1;
}
ZEND_OP1(opline).var = NUM_VAR(map_T[currT]);
}
/* Skip OP_DATA */
if (opline->opcode == ZEND_OP_DATA &&
(opline-1)->opcode == ZEND_ASSIGN_DIM) {
opline--;
continue;
}
if ((ZEND_OP2_TYPE(opline) & (IS_VAR | IS_TMP_VAR))
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
|| ((op_const_means_class[opline->opcode] & OP2_CONST_IS_CLASS) && ZEND_OP2_TYPE(opline) == IS_CONST)
#endif
) {
currT = VAR_NUM(ZEND_OP2(opline).var);
if (!valid_T[currT]) {
GET_AVAILABLE_T();
map_T[currT] = i;
valid_T[currT] = 1;
}
ZEND_OP2(opline).var = NUM_VAR(map_T[currT]);
}
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
if ((op_const_means_class[opline->opcode] & EXT_CONST_IS_CLASS)) {
#else
if (opline->opcode == ZEND_DECLARE_INHERITED_CLASS ||
opline->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED) {
#endif
currT = VAR_NUM(opline->extended_value);
if (!valid_T[currT]) {
GET_AVAILABLE_T();
map_T[currT] = i;
valid_T[currT] = 1;
}
opline->extended_value = NUM_VAR(map_T[currT]);
}
/* Allocate OP_DATA->op2 after "opernds", but before "result" */
if (opline->opcode == ZEND_ASSIGN_DIM &&
(opline+1)->opcode == ZEND_OP_DATA &&
ZEND_OP2_TYPE(opline+1) & (IS_VAR | IS_TMP_VAR)) {
currT = VAR_NUM(ZEND_OP2(opline+1).var);
GET_AVAILABLE_T();
map_T[currT] = i;
valid_T[currT] = 1;
taken_T[i] = 0;
ZEND_OP2(opline+1).var = NUM_VAR(i);
var_to_free = i;
}
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR | IS_CONST)) {
if (!(op_const_means_class[opline->opcode] & RESULT_IS_UNUSED)) {
#else
if (ZEND_RESULT_TYPE(opline) & (IS_VAR | IS_TMP_VAR)) {
#endif
currT = VAR_NUM(ZEND_RESULT(opline).var);
if (valid_T[currT]) {
if (start_of_T[currT] == opline) {
taken_T[map_T[currT]] = 0;
}
ZEND_RESULT(opline).var = NUM_VAR(map_T[currT]);
} else { /* Au still needs to be assigned a T which is a bit dumb. Should consider changing Zend */
GET_AVAILABLE_T();
if (RESULT_UNUSED(opline)) {
taken_T[i] = 0;
} else {
/* Code which gets here is using a wrongly built opcode such as RECV() */
map_T[currT] = i;
valid_T[currT] = 1;
}
ZEND_RESULT(opline).var = NUM_VAR(i);
}
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
}
#endif
}
if (var_to_free >= 0) {
taken_T[var_to_free] = 0;
var_to_free = -1;
}
opline--;
}
efree(taken_T);
efree(start_of_T);
efree(valid_T);
efree(map_T);
op_array->T = max+1;
}

3
Optimizer/pass10.c

@ -0,0 +1,3 @@
if (((ZEND_OPTIMIZER_PASS_10|ZEND_OPTIMIZER_PASS_5) & OPTIMIZATION_LEVEL) == ZEND_OPTIMIZER_PASS_10) {
nop_removal(op_array);
}

391
Optimizer/pass1_5.c

@ -0,0 +1,391 @@
/* pass 1
* - substitute persistent constants (true, false, null, etc)
* - perform compile-time evaluation of constant binary and unary operations
* - optimize series of ADD_STRING and/or ADD_CHAR
* - convert CAST(IS_BOOL,x) into BOOL(x)
* - convert INTI_FCALL_BY_NAME, DO_FCALL_BY_NAME into DO_FCALL
*/
if (ZEND_OPTIMIZER_PASS_1 & OPTIMIZATION_LEVEL) {
int i=0;
zend_op *opline = op_array->opcodes;
zend_op *end = opline + op_array->last;
while (opline<end) {
switch (opline->opcode) {
case ZEND_ADD:
case ZEND_SUB:
case ZEND_MUL:
case ZEND_DIV:
case ZEND_MOD:
case ZEND_SL:
case ZEND_SR:
case ZEND_CONCAT:
case ZEND_IS_EQUAL:
case ZEND_IS_NOT_EQUAL:
case ZEND_IS_SMALLER:
case ZEND_IS_SMALLER_OR_EQUAL:
case ZEND_IS_IDENTICAL:
case ZEND_IS_NOT_IDENTICAL:
case ZEND_BW_OR:
case ZEND_BW_AND:
case ZEND_BW_XOR:
case ZEND_BOOL_XOR:
if (ZEND_OP1_TYPE(opline)==IS_CONST
&& ZEND_OP2_TYPE(opline)==IS_CONST) {
/* binary operation wheit constant operands */
int (*binary_op)(zval *result, zval *op1, zval *op2 TSRMLS_DC) = get_binary_op(opline->opcode);
zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */
zval result;
zend_op *tmp_opline;
int er;
if (opline->opcode == ZEND_DIV &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_LONG &&
Z_LVAL(ZEND_OP2_LITERAL(opline)) == 0) {
/* div by 0 */
break;
}
er = EG(error_reporting);
EG(error_reporting) = 0;
/* evaluate constant expression */
if (binary_op(&result, &ZEND_OP1_LITERAL(opline), &ZEND_OP2_LITERAL(opline) TSRMLS_CC) != SUCCESS) {
EG(error_reporting) = er;
break;
}
EG(error_reporting) = er;
PZ_SET_REFCOUNT_P(&result, 1);
PZ_UNSET_ISREF_P(&result);
literal_dtor(&ZEND_OP1_LITERAL(opline));
literal_dtor(&ZEND_OP2_LITERAL(opline));
MAKE_NOP(opline);
/* substitute the following TMP_VAR usage with constant */
for (tmp_opline=opline+1; tmp_opline<end; tmp_opline++) {
if (ZEND_OP1_TYPE(tmp_opline)== IS_TMP_VAR
&& ZEND_OP1(tmp_opline).var == tv) {
if (tmp_opline->opcode==ZEND_FREE) {
MAKE_NOP(tmp_opline);
zval_dtor(&result);
} else {
ZEND_OP1_TYPE(tmp_opline) = IS_CONST;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
tmp_opline->op1.constant = zend_add_literal(op_array, &result TSRMLS_CC);
if (Z_TYPE(result) == IS_STRING) {
Z_HASH_P(&ZEND_OP1_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP1_LITERAL(tmp_opline))+1);
if (tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
tmp_opline->opcode == ZEND_DO_FCALL ||
tmp_opline->opcode == ZEND_CATCH ||
tmp_opline->opcode == ZEND_FETCH_CONSTANT) {
op_array->literals[tmp_opline->op1.constant].cache_slot = op_array->last_cache_slot++;
}
}
#else
ZEND_OP1_LITERAL(tmp_opline) = result;
#endif
}
/* TMP_VAR my be used only once */
break;
}
if (ZEND_OP2_TYPE(tmp_opline)== IS_TMP_VAR
&& ZEND_OP2(tmp_opline).var == tv) {
ZEND_OP2_TYPE(tmp_opline) = IS_CONST;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
tmp_opline->op2.constant = zend_add_literal(op_array, &result TSRMLS_CC);
if (Z_TYPE(result) == IS_STRING) {
Z_HASH_P(&ZEND_OP2_LITERAL(tmp_opline)) = zend_hash_func(Z_STRVAL(ZEND_OP2_LITERAL(tmp_opline)), Z_STRLEN(ZEND_OP2_LITERAL(tmp_opline))+1);
if (tmp_opline->opcode == ZEND_FETCH_R ||
tmp_opline->opcode == ZEND_FETCH_W ||
tmp_opline->opcode == ZEND_FETCH_RW ||
tmp_opline->opcode == ZEND_FETCH_IS ||
tmp_opline->opcode == ZEND_FETCH_UNSET ||
tmp_opline->opcode == ZEND_FETCH_FUNC_ARG ||
tmp_opline->opcode == ZEND_FETCH_CLASS ||
tmp_opline->opcode == ZEND_INIT_FCALL_BY_NAME ||
tmp_opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME ||
tmp_opline->opcode == ZEND_UNSET_VAR ||
tmp_opline->opcode == ZEND_ISSET_ISEMPTY_VAR ||
tmp_opline->opcode == ZEND_ADD_INTERFACE ||
tmp_opline->opcode == ZEND_ADD_TRAIT) {
op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot++;
} else if (tmp_opline->opcode == ZEND_INIT_METHOD_CALL ||
tmp_opline->opcode == ZEND_INIT_STATIC_METHOD_CALL ||
tmp_opline->opcode == ZEND_FETCH_CONSTANT ||
tmp_opline->opcode == ZEND_ASSIGN_OBJ ||
tmp_opline->opcode == ZEND_FETCH_OBJ_R ||
tmp_opline->opcode == ZEND_FETCH_OBJ_W ||
tmp_opline->opcode == ZEND_FETCH_OBJ_RW ||
tmp_opline->opcode == ZEND_FETCH_OBJ_IS ||
tmp_opline->opcode == ZEND_FETCH_OBJ_UNSET ||
tmp_opline->opcode == ZEND_FETCH_OBJ_FUNC_ARG ||
tmp_opline->opcode == ZEND_UNSET_OBJ ||
tmp_opline->opcode == ZEND_PRE_INC_OBJ ||
tmp_opline->opcode == ZEND_PRE_DEC_OBJ ||
tmp_opline->opcode == ZEND_POST_INC_OBJ ||
tmp_opline->opcode == ZEND_POST_DEC_OBJ ||
tmp_opline->opcode == ZEND_ISSET_ISEMPTY_PROP_OBJ) {
op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot;
op_array->last_cache_slot += 2;
} else if (tmp_opline->opcode == ZEND_ASSIGN_ADD ||
tmp_opline->opcode == ZEND_ASSIGN_SUB ||
tmp_opline->opcode == ZEND_ASSIGN_MUL ||
tmp_opline->opcode == ZEND_ASSIGN_DIV ||
tmp_opline->opcode == ZEND_ASSIGN_MOD ||
tmp_opline->opcode == ZEND_ASSIGN_SL ||
tmp_opline->opcode == ZEND_ASSIGN_SR ||
tmp_opline->opcode == ZEND_ASSIGN_CONCAT ||
tmp_opline->opcode == ZEND_ASSIGN_BW_OR ||
tmp_opline->opcode == ZEND_ASSIGN_BW_AND ||
tmp_opline->opcode == ZEND_ASSIGN_BW_XOR) {
if (tmp_opline->extended_value == ZEND_ASSIGN_OBJ) {
op_array->literals[tmp_opline->op2.constant].cache_slot = op_array->last_cache_slot;
op_array->last_cache_slot += 2;
}
}
}
#else
ZEND_OP2_LITERAL(tmp_opline) = result;
#endif
break;
}
}
}
break;
case ZEND_CAST:
if (ZEND_OP1_TYPE(opline)==IS_CONST &&
opline->extended_value != IS_ARRAY &&
opline->extended_value != IS_OBJECT &&
opline->extended_value != IS_RESOURCE) {
/* cast of constant operand */
zval res;
res = ZEND_OP1_LITERAL(opline);
zval_copy_ctor(&res);
switch (opline->extended_value) {
case IS_NULL:
convert_to_null(&res);
break;
case IS_BOOL:
convert_to_boolean(&res);
break;
case IS_LONG:
convert_to_long(&res);
break;
case IS_DOUBLE:
convert_to_double(&res);
break;
case IS_STRING:
convert_to_string(&res);
break;
}
literal_dtor(&ZEND_OP1_LITERAL(opline));
opline->opcode = ZEND_QM_ASSIGN;
opline->extended_value = 0;
ZEND_OP1_LITERAL(opline) = res;
SET_UNUSED(opline->op2);
} else if (opline->extended_value == IS_BOOL) {
/* T = CAST(X, IS_BOOL) => T = BOOL(X) */
opline->opcode = ZEND_BOOL;
opline->extended_value = 0;
}
break;
case ZEND_BW_NOT:
case ZEND_BOOL_NOT:
if (ZEND_OP1_TYPE(opline)==IS_CONST) {
/* unary operation on constant operand */
unary_op_type unary_op = get_unary_op(opline->opcode);
zval result;
zend_op *tmp_opline;
zend_uint tv = ZEND_RESULT(opline).var; /* temporary variable */
int er;
er = EG(error_reporting);
EG(error_reporting) = 0;
if (unary_op(&result, &ZEND_OP1_LITERAL(opline) TSRMLS_CC) != SUCCESS) {
EG(error_reporting) = er;
break;
}
EG(error_reporting) = er;
PZ_SET_REFCOUNT_P(&result, 1);
PZ_UNSET_ISREF_P(&result);
literal_dtor(&ZEND_OP1_LITERAL(opline));
MAKE_NOP(opline);
/* substitute the following TMP_VAR usage with constant */
for (tmp_opline=opline+1; tmp_opline<end; tmp_opline++) {
if (ZEND_OP1_TYPE(tmp_opline)== IS_TMP_VAR
&& ZEND_OP1(tmp_opline).var == tv) {
if (tmp_opline->opcode==ZEND_FREE) {
MAKE_NOP(tmp_opline);
zval_dtor(&result);
} else {
ZEND_OP1_TYPE(tmp_opline) = IS_CONST;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
tmp_opline->op1.constant = zend_add_literal(op_array, &result TSRMLS_CC);
#else
ZEND_OP1_LITERAL(tmp_opline) = result;
#endif
}
break;
}
if (ZEND_OP2_TYPE(tmp_opline)== IS_TMP_VAR
&& ZEND_OP2(tmp_opline).var == tv) {
ZEND_OP2_TYPE(tmp_opline) = IS_CONST;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
tmp_opline->op2.constant = zend_add_literal(op_array, &result TSRMLS_CC);
#else
ZEND_OP2_LITERAL(tmp_opline) = result;
#endif
break;
}
}
}
break;
case ZEND_ADD_STRING:
case ZEND_ADD_CHAR:
{
zend_op *next_op = opline+1;
int requires_conversion = (opline->opcode==ZEND_ADD_CHAR?1:0);
size_t final_length = 0;
char *ptr;
zend_op *last_op;
/* There is always a ZEND_RETURN at the end
if (next_op>=end) {
break;
}
*/
while (next_op->opcode == ZEND_ADD_STRING || next_op->opcode == ZEND_ADD_CHAR) {
if (ZEND_RESULT(opline).var != ZEND_RESULT(next_op).var) {
break;
}
if (next_op->opcode == ZEND_ADD_CHAR) {
final_length += 1;
} else { /* ZEND_ADD_STRING */
final_length += ZEND_OP2_LITERAL(next_op).value.str.len;
}
next_op++;
}
if (final_length == 0) {
break;
}
last_op = next_op;
final_length += (requires_conversion ? 1 : ZEND_OP2_LITERAL(opline).value.str.len);
ptr = (char *) emalloc(final_length+1);
ptr[final_length] = '\0';
if (requires_conversion) { /* ZEND_ADD_CHAR */
char chval = (char)ZEND_OP2_LITERAL(opline).value.lval;
ZEND_OP2_LITERAL(opline).value.str.val = ptr;
ptr[0] = chval;
ZEND_OP2_LITERAL(opline).type = IS_STRING;
opline->opcode = ZEND_ADD_STRING;
ptr++;
} else { /* ZEND_ADD_STRING */
memcpy(ptr, Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)));
if (!IS_INTERNED(Z_STRVAL(ZEND_OP2_LITERAL(opline)))) {
efree(Z_STRVAL(ZEND_OP2_LITERAL(opline)));
}
Z_STRVAL(ZEND_OP2_LITERAL(opline)) = ptr;
ptr += Z_STRLEN(ZEND_OP2_LITERAL(opline));
}
ZEND_OP2_LITERAL(opline).value.str.len = final_length;
next_op = opline+1;
while (next_op < last_op) {
if (next_op->opcode == ZEND_ADD_STRING) {
memcpy(ptr, ZEND_OP2_LITERAL(next_op).value.str.val, ZEND_OP2_LITERAL(next_op).value.str.len);
ptr += ZEND_OP2_LITERAL(next_op).value.str.len;
literal_dtor(&ZEND_OP2_LITERAL(next_op));
} else { /* ZEND_ADD_CHAR */
*ptr = (char)ZEND_OP2_LITERAL(next_op).value.lval;
ptr++;
}
MAKE_NOP(next_op);
next_op++;
}
if (!((ZEND_OPTIMIZER_PASS_5|ZEND_OPTIMIZER_PASS_10) & OPTIMIZATION_LEVEL)) {
/* NOP removel is disabled => insert JMP over NOPs */
if (last_op-opline >= 3) { /* If we have more than 2 NOPS then JMP over them */
(opline+1)->opcode = ZEND_JMP;
ZEND_OP1(opline+1).opline_num = last_op - op_array->opcodes; /* that's OK even for ZE2, since opline_num's are resolved in pass 2 later */
}
}
}
break;
case ZEND_FETCH_CONSTANT:
if (ZEND_OP1_TYPE(opline) == IS_UNUSED &&
ZEND_OP2_TYPE(opline) == IS_CONST &&
Z_TYPE(ZEND_OP2_LITERAL(opline)) == IS_STRING &&
Z_STRLEN(ZEND_OP2_LITERAL(opline)) == sizeof("__COMPILER_HALT_OFFSET__")-1 &&
memcmp(Z_STRVAL(ZEND_OP2_LITERAL(opline)), "__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1) == 0) {
/* substitute __COMPILER_HALT_OFFSET__ constant */
zend_bool orig_in_execution = EG(in_execution);
zend_op_array *orig_op_array = EG(active_op_array);
zval offset;
EG(in_execution) = 1;
EG(active_op_array) = op_array;
if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1, &offset TSRMLS_CC)) {
literal_dtor(&ZEND_OP2_LITERAL(opline));
ZEND_OP1_TYPE(opline) = IS_CONST;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
opline->op1.constant = zend_add_literal(op_array, &offset TSRMLS_CC);
#else
ZEND_OP1_LITERAL(opline) = offset;
#endif
SET_UNUSED(opline->op2);
opline->opcode = ZEND_QM_ASSIGN;
}
EG(active_op_array) = orig_op_array;
EG(in_execution) = orig_in_execution;
break;
}
if (ZEND_OP1_TYPE(opline) == IS_UNUSED &&
ZEND_OP2_TYPE(opline) == IS_CONST &&
ZEND_OP2_LITERAL(opline).type == IS_STRING) {
/* substitute persistent constants */
zval c;
if(zend_get_persistent_constant(Z_STRVAL(ZEND_OP2_LITERAL(opline)), Z_STRLEN(ZEND_OP2_LITERAL(opline)), &c, 1 TSRMLS_CC) == 0) {
break;
}
literal_dtor(&ZEND_OP2_LITERAL(opline));
ZEND_OP1_TYPE(opline) = IS_CONST;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
opline->op1.constant = zend_add_literal(op_array, &c TSRMLS_CC);
#else
ZEND_OP1_LITERAL(opline) = c;
#endif
SET_UNUSED(opline->op2);
opline->opcode = ZEND_QM_ASSIGN;
}
break;
case ZEND_INIT_FCALL_BY_NAME:
if(opline->extended_value == 0 /* not method */ &&
ZEND_OP1_TYPE(opline) == IS_UNUSED &&
ZEND_OP2_TYPE(opline) == IS_CONST) {
if((opline+1)->opcode == ZEND_DO_FCALL_BY_NAME &&
(opline+1)->extended_value == 0) {
(opline+1)->opcode = ZEND_DO_FCALL;
COPY_NODE((opline+1)->op1, opline->op2);
zend_str_tolower(Z_STRVAL(ZEND_OP1_LITERAL(opline+1)), Z_STRLEN(ZEND_OP1_LITERAL(opline+1)));
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
Z_HASH_P(&ZEND_OP1_LITERAL(opline+1)) = zend_hash_func(Z_STRVAL(ZEND_OP1_LITERAL(opline+1)), Z_STRLEN(ZEND_OP1_LITERAL(opline+1))+1);
op_array->literals[(opline+1)->op1.constant].cache_slot = op_array->last_cache_slot++;
#endif
MAKE_NOP(opline);
}
}
break;
}
opline++;
i++;
}
}

210
Optimizer/pass2.c

@ -0,0 +1,210 @@
/* pass 2:
* - convert non-numeric constants to numeric constants in numeric operators
* - optimize constant conditional JMPs
* - optimize static BRKs and CONTs
*/
if (ZEND_OPTIMIZER_PASS_2 & OPTIMIZATION_LEVEL) {
zend_op *opline;
zend_op *end = op_array->opcodes+op_array->last;
opline = op_array->opcodes;
while (opline<end) {
switch (opline->opcode) {
case ZEND_ADD:
case ZEND_SUB:
case ZEND_MUL:
case ZEND_DIV:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
if (ZEND_OP1_LITERAL(opline).type == IS_STRING) {
convert_scalar_to_number(&ZEND_OP1_LITERAL(opline) TSRMLS_CC);
}
}
/* break missing *intentionally* - the assign_op's may only optimize op2 */
case ZEND_ASSIGN_ADD:
case ZEND_ASSIGN_SUB:
case ZEND_ASSIGN_MUL:
case ZEND_ASSIGN_DIV:
if(opline->extended_value != 0) {
/* object tristate op - don't attempt to optimize it! */
break;
}
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
if (ZEND_OP2_LITERAL(opline).type == IS_STRING) {
convert_scalar_to_number(&ZEND_OP2_LITERAL(opline) TSRMLS_CC);
}
}
break;
case ZEND_MOD:
case ZEND_SL:
case ZEND_SR:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
if (ZEND_OP1_LITERAL(opline).type != IS_LONG) {
convert_to_long(&ZEND_OP1_LITERAL(opline));
}
}
/* break missing *intentionally - the assign_op's may only optimize op2 */
case ZEND_ASSIGN_MOD:
case ZEND_ASSIGN_SL:
case ZEND_ASSIGN_SR:
if(opline->extended_value != 0) {
/* object tristate op - don't attempt to optimize it! */
break;
}
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
if (ZEND_OP2_LITERAL(opline).type != IS_LONG) {
convert_to_long(&ZEND_OP2_LITERAL(opline));
}
}
break;
case ZEND_CONCAT:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
if (ZEND_OP1_LITERAL(opline).type != IS_STRING) {
convert_to_string(&ZEND_OP1_LITERAL(opline));
}
}
/* break missing *intentionally - the assign_op's may only optimize op2 */
case ZEND_ASSIGN_CONCAT:
if(opline->extended_value != 0) {
/* object tristate op - don't attempt to optimize it! */
break;
}
if (ZEND_OP2_TYPE(opline) == IS_CONST) {
if (ZEND_OP2_LITERAL(opline).type != IS_STRING) {
convert_to_string(&ZEND_OP2_LITERAL(opline));
}
}
break;
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
/* convert Ti = JMPZ_EX(Ti, L) to JMPZ(Ti, L) */
if (ZEND_OP1_TYPE(opline) == IS_TMP_VAR &&
ZEND_RESULT_TYPE(opline) == IS_TMP_VAR &&
ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
opline->opcode -= 3;
/* convert Ti = JMPZ_EX(C, L) => Ti = QM_ASSIGN(C)
in case we know it wouldn't jump */
} else if (ZEND_OP1_TYPE(opline) == IS_CONST) {
int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
if (opline->opcode == ZEND_JMPZ_EX) {
should_jmp = !should_jmp;
}
if (!should_jmp) {
opline->opcode = ZEND_QM_ASSIGN;
SET_UNUSED(opline->op2);
}
}
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
int should_jmp = zend_is_true(&ZEND_OP1_LITERAL(opline));
if (opline->opcode == ZEND_JMPZ) {
should_jmp = !should_jmp;
}
literal_dtor(&ZEND_OP1_LITERAL(opline));
ZEND_OP1_TYPE(opline) = IS_UNUSED;
if (should_jmp) {
opline->opcode = ZEND_JMP;
COPY_NODE(opline->op1, opline->op2);
} else {
MAKE_NOP(opline);
}
break;
}
if ((opline+1)->opcode == ZEND_JMP) {
/* JMPZ(X, L1), JMP(L2) => JMPZNZ(X, L1, L2) */
/* JMPNZ(X, L1), JMP(L2) => JMPZNZ(X, L2, L1) */
if (ZEND_OP2(opline).opline_num == ZEND_OP1(opline+1).opline_num) {
/* JMPZ(X, L1), JMP(L1) => NOP, JMP(L1) */
MAKE_NOP(opline);
} else {
if (opline->opcode == ZEND_JMPZ) {
opline->extended_value = ZEND_OP1(opline+1).opline_num;
} else {
opline->extended_value = ZEND_OP2(opline).opline_num;
COPY_NODE(opline->op2, (opline+1)->op1);
}
opline->opcode = ZEND_JMPZNZ;
}
}
break;
case ZEND_JMPZNZ:
if (ZEND_OP1_TYPE(opline) == IS_CONST) {
int opline_num;
if (zend_is_true(&ZEND_OP1_LITERAL(opline))) {
opline_num = opline->extended_value; /* JMPNZ */
} else {
opline_num = ZEND_OP2(opline).opline_num; /* JMPZ */
}
literal_dtor(&ZEND_OP1_LITERAL(opline));
ZEND_OP1(opline).opline_num = opline_num;
ZEND_OP1_TYPE(opline) = IS_UNUSED;
opline->opcode = ZEND_JMP;
}
break;
case ZEND_BRK:
case ZEND_CONT:
{
zend_brk_cont_element *jmp_to;
int array_offset;
int nest_levels;
int dont_optimize=0;
if (ZEND_OP2_TYPE(opline) != IS_CONST) {
break;
}
convert_to_long(&ZEND_OP2_LITERAL(opline));
nest_levels = ZEND_OP2_LITERAL(opline).value.lval;
array_offset = ZEND_OP1(opline).opline_num;
while (1) {
if (array_offset==-1) {
dont_optimize=1; /* don't optimize this bogus break/continue, let the executor shout */
break;
}
jmp_to = &op_array->brk_cont_array[array_offset];
array_offset = jmp_to->parent;
if (--nest_levels > 0) {
if (opline->opcode == ZEND_BRK &&
(op_array->opcodes[jmp_to->brk].opcode == ZEND_FREE ||
op_array->opcodes[jmp_to->brk].opcode == ZEND_SWITCH_FREE)) {
dont_optimize=1;
break;
}
} else {
break;
}
}
if (dont_optimize) {
break;
}
/* optimize - convert to a JMP */
switch (opline->opcode) {
case ZEND_BRK:
MAKE_NOP(opline);
ZEND_OP1(opline).opline_num = jmp_to->brk;
break;
case ZEND_CONT:
MAKE_NOP(opline);
ZEND_OP1(opline).opline_num = jmp_to->cont;
break;
}
opline->opcode = ZEND_JMP;
/* MAKE_NOP() already set op1 and op2 to IS_UNUSED */
}
break;
}
opline++;
}
}

443
Optimizer/pass3.c

@ -0,0 +1,443 @@
/* pass 3:
* - optimize $i = $i+expr to $i+=expr
* - optimize series of JMPs
* - change $i++ to ++$i where possible
*/
/* compares opcodes with allowing oc1 be _EX of oc2 */
#define SAME_OPCODE_EX(oc1, oc2) ((oc1 == oc2) || (oc1 == ZEND_JMPZ_EX && oc2 == ZEND_JMPZ) || (oc1 == ZEND_JMPNZ_EX && oc2 == ZEND_JMPNZ))
/* we use "jmp_hitlist" to avoid infinity loops during jmp optimization */
#define CHECK_JMP(target, label) \
for (i=0; i<jmp_hitlist_count; i++) { \
if (jmp_hitlist[i] == ZEND_OP1(&op_array->opcodes[target]).opline_num) { \
goto label; \
} \
} \
jmp_hitlist[jmp_hitlist_count++] = ZEND_OP1(&op_array->opcodes[target]).opline_num;
#define CHECK_JMP2(target, label) \
for (i=0; i<jmp_hitlist_count; i++) { \
if (jmp_hitlist[i] == ZEND_OP2(&op_array->opcodes[target]).opline_num) { \
goto label; \
} \
} \
jmp_hitlist[jmp_hitlist_count++] = ZEND_OP2(&op_array->opcodes[target]).opline_num;
if(ZEND_OPTIMIZER_PASS_3 & OPTIMIZATION_LEVEL) {
zend_op *opline;
zend_op *end = op_array->opcodes+op_array->last;
zend_uint *jmp_hitlist;
int jmp_hitlist_count;
int i;
zend_uint opline_num = 0;
ALLOCA_FLAG(use_heap);
jmp_hitlist = (zend_uint *) DO_ALLOCA(sizeof(zend_uint)*op_array->last);
opline = op_array->opcodes;
while (opline<end) {
jmp_hitlist_count = 0;
switch (opline->opcode) {
case ZEND_ADD:
case ZEND_SUB:
case ZEND_MUL:
case ZEND_DIV:
case ZEND_MOD:
case ZEND_CONCAT:
case ZEND_SL:
case ZEND_SR:
case ZEND_BW_OR:
case ZEND_BW_AND:
case ZEND_BW_XOR:
{
zend_op *next_opline = opline+1;
while (next_opline < end && next_opline->opcode == ZEND_NOP) {
++next_opline;
}
if (next_opline >= end || next_opline->opcode != ZEND_ASSIGN) {
break;
}
if ((ZEND_OP2_TYPE(opline)==IS_VAR || ZEND_OP2_TYPE(opline)==IS_CV)
&& ZEND_OP2(opline).var == ZEND_OP1(next_opline).var &&
(opline->opcode == ZEND_ADD ||
opline->opcode == ZEND_MUL ||
opline->opcode == ZEND_BW_OR ||
opline->opcode == ZEND_BW_AND ||
opline->opcode == ZEND_BW_XOR)) {
/* change $i=expr+$i to $i=$i+expr so that the next
* optimization works on it
*/
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
zend_uchar tmp_type=opline->op1_type;
znode_op tmp=opline->op1;
#else
znode tmp=opline->op1;
#endif
if(opline->opcode != ZEND_ADD || ZEND_OP1_TYPE(opline) == IS_CONST) {
/* protection from array add: $a = array + $a is not commutative! */
COPY_NODE(opline->op1, opline->op2);
COPY_NODE(opline->op2, tmp);
}
}
if ((ZEND_OP1_TYPE(opline)==IS_VAR || ZEND_OP1_TYPE(opline)==IS_CV)
&& ZEND_OP1(opline).var == ZEND_OP1(next_opline).var
&& ZEND_OP1_TYPE(opline) == ZEND_OP1_TYPE(next_opline)) {
switch (opline->opcode) {
case ZEND_ADD:
opline->opcode = ZEND_ASSIGN_ADD;
break;
case ZEND_SUB:
opline->opcode = ZEND_ASSIGN_SUB;
break;
case ZEND_MUL:
opline->opcode = ZEND_ASSIGN_MUL;
break;
case ZEND_DIV:
opline->opcode = ZEND_ASSIGN_DIV;
break;
case ZEND_MOD:
opline->opcode = ZEND_ASSIGN_MOD;
break;
case ZEND_CONCAT:
opline->opcode = ZEND_ASSIGN_CONCAT;
break;
case ZEND_SL:
opline->opcode = ZEND_ASSIGN_SL;
break;
case ZEND_SR:
opline->opcode = ZEND_ASSIGN_SR;
break;
case ZEND_BW_OR:
opline->opcode = ZEND_ASSIGN_BW_OR;
break;
case ZEND_BW_AND:
opline->opcode = ZEND_ASSIGN_BW_AND;
break;
case ZEND_BW_XOR:
opline->opcode = ZEND_ASSIGN_BW_XOR;
break;
}
COPY_NODE(opline->result, next_opline->result);
MAKE_NOP(next_opline);
opline++;
opline_num++;
}
}
break;
case ZEND_JMP:
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
if (op_array->has_finally_block) {
break;
}
#endif
/* convert L: JMP L+1 to NOP */
if(ZEND_OP1(opline).opline_num == opline_num + 1) {
MAKE_NOP(opline);
goto done_jmp_optimization;
}
/* convert JMP L1 ... L1: JMP L2 to JMP L2 .. L1: JMP L2 */
while (ZEND_OP1(opline).opline_num<op_array->last
&& op_array->opcodes[ZEND_OP1(opline).opline_num].opcode == ZEND_JMP) {
int target = ZEND_OP1(opline).opline_num;
CHECK_JMP(target, done_jmp_optimization);
ZEND_OP1(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
}
break;
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
case ZEND_JMP_SET:
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
case ZEND_JMP_SET_VAR:
#endif
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
if (op_array->has_finally_block) {
break;
}
#endif
while (ZEND_OP2(opline).opline_num<op_array->last) {
int target = ZEND_OP2(opline).opline_num;
if (op_array->opcodes[target].opcode == ZEND_JMP) {
ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
} else {
break;
}
}
break;
#endif
case ZEND_JMPZ:
case ZEND_JMPNZ:
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
if (op_array->has_finally_block) {
break;
}
#endif
/* convert L: JMPZ L+1 to NOP */
if(ZEND_OP2(opline).opline_num == opline_num + 1) {
MAKE_NOP(opline);
goto done_jmp_optimization;
}
while (ZEND_OP2(opline).opline_num<op_array->last) {
int target = ZEND_OP2(opline).opline_num;
if (op_array->opcodes[target].opcode == ZEND_JMP) {
/* plain JMP */
/* JMPZ(X,L1), L1: JMP(L2) => JMPZ(X,L2), L1: JMP(L2) */
CHECK_JMP(target, done_jmp_optimization);
ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
} else if (op_array->opcodes[target].opcode == opline->opcode &&
SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
/* same opcode and same var as this opcode */
/* JMPZ(X,L1), L1: JMPZ(X,L2) => JMPZ(X,L2), L1: JMPZ(X,L2) */
CHECK_JMP2(target, done_jmp_optimization);
ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;
} else if (op_array->opcodes[target].opcode == opline->opcode+3 &&
SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
/* convert JMPZ(X,L1), L1: T JMPZ_EX(X,L2) to
T = JMPZ_EX(X, L2) */
ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;opline->opcode += 3;
COPY_NODE(opline->result, op_array->opcodes[target].result);
break;
} else if (op_array->opcodes[target].opcode == INV_COND(opline->opcode) &&
SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
/* convert JMPZ(X,L1), L1: JMPNZ(X,L2) to
JMPZ(X,L1+1) */
ZEND_OP2(opline).opline_num = target+1;
break;
} else if (op_array->opcodes[target].opcode == INV_COND_EX(opline->opcode) &&
SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
/* convert JMPZ(X,L1), L1: T = JMPNZ_EX(X,L2) to
T = JMPZ_EX(X,L1+1) */
ZEND_OP2(opline).opline_num = target+1;
opline->opcode += 3;
COPY_NODE(opline->result, op_array->opcodes[target].result);
break;
} else {
break;
}
}
break;
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX: {
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
zend_uchar T_type = opline->result_type;
znode_op T = opline->result;
#else
znode T = opline->result;
#endif
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
if (op_array->has_finally_block) {
break;
}
#endif
/* convert L: T = JMPZ_EX X,L+1 to T = BOOL(X) */
/* convert L: T = JMPZ_EX T,L+1 to NOP */
if(ZEND_OP2(opline).opline_num == opline_num + 1) {
if(ZEND_OP1(opline).var == ZEND_RESULT(opline).var) {
MAKE_NOP(opline);
} else {
opline->opcode = ZEND_BOOL;
SET_UNUSED(opline->op2);
}
goto done_jmp_optimization;
}
while (ZEND_OP2(opline).opline_num<op_array->last) {
int target = ZEND_OP2(opline).opline_num;
if(SAME_OPCODE_EX(opline->opcode, op_array->opcodes[target].opcode) &&
SAME_VAR(op_array->opcodes[target].op1, T)) {
/* Check for JMPZ_EX to JMPZ[_EX] with the same condition, either with _EX or not */
if(op_array->opcodes[target].opcode == opline->opcode) {
/* change T only if we have _EX opcode there */
COPY_NODE(T, op_array->opcodes[target].result);
}
CHECK_JMP2(target, continue_jmp_ex_optimization);
ZEND_OP2(opline).opline_num = ZEND_OP2(&op_array->opcodes[target]).opline_num;
} else if(op_array->opcodes[target].opcode == ZEND_JMPZNZ &&
SAME_VAR(op_array->opcodes[target].op1, T)) {
/* Check for JMPZNZ with same cond variable */
int new_target;
CHECK_JMP2(target, continue_jmp_ex_optimization);
if(opline->opcode == ZEND_JMPZ_EX) {
new_target = ZEND_OP2(&op_array->opcodes[target]).opline_num;
} else {
/* JMPNZ_EX */
new_target = op_array->opcodes[target].extended_value;
}
ZEND_OP2(opline).opline_num = new_target;
} else if((op_array->opcodes[target].opcode == INV_EX_COND_EX(opline->opcode) ||
op_array->opcodes[target].opcode == INV_EX_COND(opline->opcode)
) &&
SAME_VAR(opline->op1, op_array->opcodes[target].op1)) {
/* convert JMPZ_EX(X,L1), L1: JMPNZ_EX(X,L2) to
JMPZ_EX(X,L1+1) */
ZEND_OP2(opline).opline_num = target+1;
break;
} else {
break;
}
} /* while */
continue_jmp_ex_optimization:
break;
#if 0
/* If Ti = JMPZ_EX(X, L) and Ti is not used, convert to JMPZ(X, L) */
{
zend_op *op;
for(op = opline+1; op<end; op++) {
if(ZEND_RESULT_TYPE(op) == IS_TMP_VAR &&
ZEND_RESULT(op).var == ZEND_RESULT(opline).var) {
break; /* can pass to part 2 */
}
if(op->opcode == ZEND_JMP ||
op->opcode == ZEND_JMPZ ||
op->opcode == ZEND_JMPZ_EX ||
op->opcode == ZEND_JMPNZ ||
op->opcode == ZEND_JMPNZ_EX ||
op->opcode == ZEND_JMPZNZ ||
op->opcode == ZEND_BRK ||
op->opcode == ZEND_CONT ||
op->opcode == ZEND_CASE ||
op->opcode == ZEND_RETURN ||
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
op->opcode == ZEND_RETURN_BY_REF ||
#endif
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
op->opcode == ZEND_FAST_RET ||
#endif
op->opcode == ZEND_FE_FETCH ||
op->opcode == ZEND_EXIT) {
break;
}
if(ZEND_OP1_TYPE(op) == IS_TMP_VAR &&
ZEND_OP1(op).var == ZEND_RESULT(opline).var) {
goto done_jmp_optimization;
}
if(ZEND_OP2_TYPE(op) == IS_TMP_VAR &&
ZEND_OP2(op).var == ZEND_RESULT(opline).var) {
goto done_jmp_optimization;
}
} /* for */
for(op = &op_array->opcodes[ZEND_OP2(opline).opline_num]; op<end; op++) {
if(ZEND_RESULT_TYPE(op) == IS_TMP_VAR &&
ZEND_RESULT(op).var == ZEND_RESULT(opline).var) {
break; /* can pass to optimization */
}
if(op->opcode == ZEND_JMP ||
op->opcode == ZEND_JMPZ ||
op->opcode == ZEND_JMPZ_EX ||
op->opcode == ZEND_JMPNZ ||
op->opcode == ZEND_JMPNZ_EX ||
op->opcode == ZEND_JMPZNZ ||
op->opcode == ZEND_BRK ||
op->opcode == ZEND_CONT ||
op->opcode == ZEND_CASE ||
op->opcode == ZEND_RETURN ||
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
op->opcode == ZEND_RETURN_BY_REF ||
#endif
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
op->opcode == ZEND_FAST_RET ||
#endif
op->opcode == ZEND_FE_FETCH ||
op->opcode == ZEND_EXIT) {
break;
}
if(ZEND_OP1_TYPE(op) == IS_TMP_VAR &&
ZEND_OP1(op).var == ZEND_RESULT(opline).var) {
goto done_jmp_optimization;
}
if(ZEND_OP2_TYPE(op) == IS_TMP_VAR &&
ZEND_OP2(op).var == ZEND_RESULT(opline).var) {
goto done_jmp_optimization;
}
}
opline->opcode = opline->opcode-3; /* JMP_EX -> JMP */
SET_UNUSED(opline->result);
break;
}
#endif
}
break;
case ZEND_JMPZNZ:
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
if (op_array->has_finally_block) {
break;
}
#endif
/* JMPZNZ(X,L1,L2), L1: JMP(L3) => JMPZNZ(X,L3,L2), L1: JMP(L3) */
while (ZEND_OP2(opline).opline_num < op_array->last
&& op_array->opcodes[ZEND_OP2(opline).opline_num].opcode == ZEND_JMP) {
int target = ZEND_OP2(opline).opline_num;
CHECK_JMP(target, continue_jmpznz_optimization);
ZEND_OP2(opline).opline_num = ZEND_OP1(&op_array->opcodes[target]).opline_num;
}
continue_jmpznz_optimization:
/* JMPZNZ(X,L1,L2), L2: JMP(L3) => JMPZNZ(X,L1,L3), L2: JMP(L3) */
while (opline->extended_value < op_array->last
&& op_array->opcodes[opline->extended_value].opcode == ZEND_JMP) {
int target = opline->extended_value;
CHECK_JMP(target, done_jmp_optimization);
opline->extended_value = ZEND_OP1(&op_array->opcodes[target]).opline_num;
}
break;
case ZEND_POST_INC:
case ZEND_POST_DEC: {
/* POST_INC, FREE => PRE_INC */
zend_op *next_op = opline+1;
if (next_op>=end) {
break;
}
if (next_op->opcode == ZEND_FREE
&& ZEND_OP1(next_op).var == ZEND_RESULT(opline).var) {
MAKE_NOP(next_op);
switch (opline->opcode) {
case ZEND_POST_INC:
opline->opcode = ZEND_PRE_INC;
break;
case ZEND_POST_DEC:
opline->opcode = ZEND_PRE_DEC;
break;
}
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
ZEND_RESULT_TYPE(opline) = IS_VAR | EXT_TYPE_UNUSED;
#else
ZEND_RESULT_TYPE(opline) = IS_VAR;
ZEND_RESULT(opline).EA.type = 0;
ZEND_RESULT(opline).EA.type |= EXT_TYPE_UNUSED;
#endif
}
}
break;
}
done_jmp_optimization:
opline++;
opline_num++;
}
FREE_ALLOCA(jmp_hitlist);
}

3
Optimizer/pass5.c

@ -0,0 +1,3 @@
if (ZEND_OPTIMIZER_PASS_5 & OPTIMIZATION_LEVEL) {
zend_block_optimization(op_array TSRMLS_CC);
}

8
Optimizer/pass9.c

@ -0,0 +1,8 @@
/* pass 9
*
* - optimize usage of temporary variables
*/
if (ZEND_OPTIMIZER_PASS_9 & OPTIMIZATION_LEVEL) {
optimize_temporary_variables(op_array);
}

139
Optimizer/zend_optimizer.c

@ -0,0 +1,139 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include "Optimizer/zend_optimizer.h"
#include "Optimizer/zend_optimizer_internal.h"
#include "zend_API.h"
#include "zend_constants.h"
#include "zend_execute.h"
#define OPTIMIZATION_LEVEL \
ZCG(accel_directives).optimization_level
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
int zend_add_literal(zend_op_array *op_array, const zval *zv TSRMLS_DC)
{
int i = op_array->last_literal;
op_array->last_literal++;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
{
if (i >= CG(context).literals_size) {
CG(context).literals_size += 16; /* FIXME */
op_array->literals = (zend_literal*)erealloc(op_array->literals, CG(context).literals_size * sizeof(zend_literal));
}
}
#else
if (i >= op_array->size_literal) {
op_array->size_literal += 16; /* FIXME */
op_array->literals = (zend_literal*)erealloc(op_array->literals, op_array->size_literal * sizeof(zend_literal));
}
#endif
op_array->literals[i].constant = *zv;
Z_SET_REFCOUNT(op_array->literals[i].constant, 2);
Z_SET_ISREF(op_array->literals[i].constant);
return i;
}
# define LITERAL_LONG(op, val) do { \
zval _c; \
ZVAL_LONG(&_c, val); \
op.constant = zend_add_literal(op_array, &_c TSRMLS_CC); \
} while (0)
# define LITERAL_BOOL(op, val) do { \
zval _c; \
ZVAL_BOOL(&_c, val); \
op.constant = zend_add_literal(op_array, &_c TSRMLS_CC); \
} while (0)
# define literal_dtor(zv) do { \
zval_dtor(zv); \
Z_TYPE_P(zv) = IS_NULL; \
} while (0)
#define COPY_NODE(target, src) do { \
target ## _type = src ## _type; \
target = src; \
} while (0)
#else
# define LITERAL_LONG(op, val) ZVAL_LONG(&op.u.constant, val)
# define LITERAL_BOOL(op, val) ZVAL_BOOL(&op.u.constant, val)
# define literal_dtor(zv) zval_dtor(zv)
#define COPY_NODE(target, src) do { \
target = src; \
} while (0)
#endif
#include "Optimizer/nop_removal.c"
#include "Optimizer/block_pass.c"
#include "Optimizer/optimize_temp_vars_5.c"
void zend_optimizer(zend_op_array *op_array TSRMLS_DC)
{
if (op_array->type == ZEND_EVAL_CODE ||
(op_array->fn_flags & ZEND_ACC_INTERACTIVE)) {
return;
}
/* pass 1
* - substitute persistent constants (true, false, null, etc)
* - perform compile-time evaluation of constant binary and unary operations
* - optimize series of ADD_STRING and/or ADD_CHAR
* - convert CAST(IS_BOOL,x) into BOOL(x)
* - convert INTI_FCALL_BY_NAME + DO_FCALL_BY_NAME into DO_FCALL
*/
#include "Optimizer/pass1_5.c"
/* pass 2:
* - convert non-numeric constants to numeric constants in numeric operators
* - optimize constant conditional JMPs
* - optimize static BRKs and CONTs
*/
#include "Optimizer/pass2.c"
/* pass 3:
* - optimize $i = $i+expr to $i+=expr
* - optimize series of JMPs
* - change $i++ to ++$i where possible
*/
#include "Optimizer/pass3.c"
/* pass 5:
* - CFG optimization
*/
#include "Optimizer/pass5.c"
/* pass 9:
* - Optimize temp variables usage
*/
#include "Optimizer/pass9.c"
/* pass 10:
* - remove NOPs
*/
#include "Optimizer/pass10.c"
}

49
Optimizer/zend_optimizer.h

@ -0,0 +1,49 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_OPTIMIZER_H
#define ZEND_OPTIMIZER_H
#include "zend.h"
#include "zend_compile.h"
#define ZEND_OPTIMIZER_PASS_1 (1<<0) /* CSE, STRING construction */
#define ZEND_OPTIMIZER_PASS_2 (1<<1) /* Constant conversion and jums */
#define ZEND_OPTIMIZER_PASS_3 (1<<2) /* ++, +=, series of jumps */
#define ZEND_OPTIMIZER_PASS_4 (1<<3)
#define ZEND_OPTIMIZER_PASS_5 (1<<4) /* CFG based optimization */
#define ZEND_OPTIMIZER_PASS_6 (1<<5)
#define ZEND_OPTIMIZER_PASS_7 (1<<6)
#define ZEND_OPTIMIZER_PASS_8 (1<<7)
#define ZEND_OPTIMIZER_PASS_9 (1<<8) /* TMP VAR usage */
#define ZEND_OPTIMIZER_PASS_10 (1<<9) /* NOP removal */
#define ZEND_OPTIMIZER_PASS_11 (1<<10)
#define ZEND_OPTIMIZER_PASS_12 (1<<11)
#define ZEND_OPTIMIZER_PASS_13 (1<<12)
#define ZEND_OPTIMIZER_PASS_14 (1<<13)
#define ZEND_OPTIMIZER_ALL_PASSES 0xFFFFFFFF
#define DEFAULT_OPTIMIZATION_LEVEL "0xFFFFFFFF"
void zend_optimizer(zend_op_array *op_array TSRMLS_DC);
#endif

76
Optimizer/zend_optimizer_internal.h

@ -0,0 +1,76 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_OPTIMIZER_INTERNAL_H
#define ZEND_OPTIMIZER_INTERNAL_H
#include "ZendAccelerator.h"
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
# define VAR_NUM(v) (EX_TMP_VAR_NUM(0, 0) - EX_TMP_VAR(0, v))
# define NUM_VAR(v) ((zend_uint)EX_TMP_VAR_NUM(0, v))
#else
# define VAR_NUM(v) ((v)/(sizeof(temp_variable)))
# define NUM_VAR(v) ((v)*(sizeof(temp_variable)))
#endif
#define INV_COND(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ : ZEND_JMPZ)
#define INV_EX_COND(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ : ZEND_JMPZ)
#define INV_COND_EX(op) ((op) == ZEND_JMPZ ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
#define INV_EX_COND_EX(op) ((op) == ZEND_JMPZ_EX ? ZEND_JMPNZ_EX : ZEND_JMPZ_EX)
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
# define MAKE_NOP(opline) { opline->opcode = ZEND_NOP; memset(&opline->result,0,sizeof(opline->result)); memset(&opline->op1,0,sizeof(opline->op1)); memset(&opline->op2,0,sizeof(opline->op2)); opline->result_type=opline->op1_type=opline->op2_type=IS_UNUSED; opline->handler = zend_opcode_handlers[ZEND_NOP]; }
# define RESULT_USED(op) (((op->result_type & IS_VAR) && !(op->result_type & EXT_TYPE_UNUSED)) || op->result_type == IS_TMP_VAR)
# define RESULT_UNUSED(op) ((op->result_type & EXT_TYPE_UNUSED) != 0)
# define SAME_VAR(op1, op2) ((((op1 ## _type & IS_VAR) && (op2 ## _type & IS_VAR)) || (op1 ## _type == IS_TMP_VAR && op2 ## _type == IS_TMP_VAR)) && op1.var == op2.var)
#else
# define MAKE_NOP(opline) { opline->opcode = ZEND_NOP; memset(&opline->result,0,sizeof(znode)); memset(&opline->op1,0,sizeof(znode)); memset(&opline->op2,0,sizeof(znode)); opline->result.op_type=opline->op1.op_type=opline->op2.op_type=IS_UNUSED; opline->handler = zend_opcode_handlers[ZEND_NOP]; }
# define RESULT_USED(op) ((op->result.op_type == IS_VAR && (op->result.u.EA.type & EXT_TYPE_UNUSED) == 0) || (op->result.op_type == IS_TMP_VAR))
# define RESULT_UNUSED(op) ((op->result.op_type == IS_VAR) && (op->result.u.EA.type == EXT_TYPE_UNUSED))
# define SAME_VAR(op1, op2) (((op1.op_type == IS_VAR && op2.op_type == IS_VAR) || (op1.op_type == IS_TMP_VAR && op2.op_type == IS_TMP_VAR)) && op1.u.var == op2.u.var)
#endif
typedef struct _zend_code_block zend_code_block;
typedef struct _zend_block_source zend_block_source;
struct _zend_code_block {
int access;
zend_op *start_opline;
int start_opline_no;
int len;
zend_code_block *op1_to;
zend_code_block *op2_to;
zend_code_block *ext_to;
zend_code_block *follow_to;
zend_code_block *next;
zend_block_source *sources;
zend_code_block **try;
zend_code_block **catch;
zend_bool is_try;
};
struct _zend_block_source {
zend_code_block *from;
zend_block_source *next;
};
#endif

171
README

@ -0,0 +1,171 @@
The Zend Optimizer+
===================
The Zend Optimizer+ provides faster PHP execution through opcode caching and
optimization. It improves PHP performance by storing precompiled script
bytecode in the shared memory. This eliminates the stages of reading code from
the disk and compiling it on future access. In addition it applies a few
bytecode optimization patterns that make code execution faster.
Compatibility
-------------
This version of Zend Optimizer+ is compatible with PHP 5.2.*, 5.3.*, 5.4.*
and PHP-5.5 development branch. PHP 5.2 support may be removed in the future.
Quick Install
-------------
- Compile
export PHP_DIR=/usr/local/php5.5
PHP_AUTOCONF=autoconf $PHP_DIR/bin/phpize
./configure \
--enable-optimizer-plus \
--with-php-config=$PHP_DIR/bin/php-config
make
- Install
cp .libs/ZendOptimizerPlus.so $PHP_DIR/lib/ZendOptimizerPlus.so
- Edit php.ini
zend_extensin=/...full path.../ZendOptimizerPlus.so
- Restart PHP
Speed Tunning
-------------
We reccomend the following configuration options for best performance.
zend_optimizerplus.memory_consumption=128
zend_optimizerplus.interned_strings_buffer=8
zend_optimizerplus.max_accelerated_files=4000
zend_optimizerplus.revalidate_freq=60
zend_optimizerplus.save_comments=0
zend_optimizerplus.fast_shutdown=1
zend_optimizerplus.enable_file_override=1
zend_optimizerplus.enable_cli=1
In some cases you may like to prefer enabling/disabling some features
to avoid incompatibilities at the cost of some performance degradation.
Configuration Directives
------------------------
zend_optimizerplus.enable (default "1")
Optimizer+ On/Off switch. When set to Off, code is not optimized.
zend_optimizerplus.memory_consumption (default "64")
The Optimizer+ shared memory storage size. The amount of memory for storing
precompiled PHP code in Mbytes.
zend_optimizerplus.interned_strings_buffer (default "4")
The amount of memory for interned strings in Mbytes.
zend_optimizerplus.max_accelerated_files (default "2000")
The maximum number of keys (scripts) in the Optimizer+ hash table.
The number is actually the the first one in the following set of prime
numbers that is bigger than the one supplied: { 223, 463, 983, 1979, 3907,
7963, 16229, 32531, 65407, 130987 }. Only numbers between 200 and 100000
are allowed.
zend_optimizerplus.max_wasted_percentage (default "5")
The maximum percentage of "wasted" memory until a restart is scheduled
zend_optimizerplus.use_cwd (default "1")
When this directive is enabled, the Optimizer+ appends the current working
directory to the script key, thus elminating possible collisions between
files with the same name (basename). Disablingthe directive improves
performance, but may break existing applications.
zend_optimizerplus.validate_timestamps (default "1")
When disabled, you must reset the Optimizer+ manually or restart the
webserver for changes to the filesystem to take effect.
The frequancy of the check is controlled by the directive
"zend_optimizerplus.revalidate_freq"
zend_optimizerplus.revalidate_freq (default "2")
How often (in seconds) to check file timestamps for changes to the shared
memory storage allocation.
zend_optimizerplus.revalidate_path (default "0")
Enables or disables file search in include_path optimization
If the file search is disabled and a cached file is found that uses
the same include_path, the file is not searched again. Thus, if a file
with the same name appears somewhere else in include_path, it
won't be found. Enable this directive if this optimization has an effect on
your applications. The default for this directive is disabled, which means
that optimization is active.
zend_optimizerplus.save_comments (default "1")
If disabled, all PHPDoc comments are dropped from the code to reduce the
size of the optimized code.
zend_optimizerplus.fast_shutdown (default "0")
If enabled, a fast shutdown sequence is used for the accelerated code
The fast shutdown sequence doesn't free each allocated block, but lets
the Zend Engine Memory Manager do the work.
zend_optimizerplus.enable_file_override (default "0")
Allow file existance override (file_exists, etc.) performance feature
zend_optimizerplus.optimization_level (default "0xffffffff")
A bitmask, where each bit enables or disables the appropriate Optimizer+
passes
zend_optimizerplus.inherited_hack (default "1")
Enable this hack as a workaround for "can't redeclare class" errors.
The Optimizer+ stores the places where DECLARE_CLASS opcodes use
inheritance (These are the only opcodes that can be executed by PHP,
but which may not be executed because the parent class is missing due to
optimization). When the file is loaded, Optimizer+ tries to bind the
inherited classes by using the current environment. The problem with this
scenario is that, while the DECLARE_CLASS opcode may not be needed for the
current script, if the script requires that the opcode at least be defined,
it may not run. The default for this directive is disabled, which means
that optimization is active. In php-5.3 and above this hack is not needed
anymore and this setting has no effect.
zend_optimizerplus.dups_fix (default "0")
Enable this hack as a workaround for "duplicate definition" errors
zend_optimizerplus.blacklist_filename
The location of the Optimizer+ blacklist file
The Optimizer+ blacklist file is a text file that holds the names of files
that should not be accelerated. The file format is to add each filename
to a new line. The filename may be a full path or just a file prefix
(i.e., /var/www/x blacklists all the files and directories in /var/www
that start with 'x'). Files are usually triggered by one of the following
three reasons:
1) Directories that contain auto generated code, like Smarty or ZFW cache.
2) Code that does not work well when accelerated, due to some delayed
compile time evaluation.
3) Code that triggers an Optimizer+ bug.
zend_optimizerplus.consistency_checks (default "0")
Check the cache checksum each N requests.
The default value of "0" means that the checks are disabled.
Because calculating the checksum impairs performance, this directive should
be enabled only as part of a debugging process.
zend_optimizerplus.force_restart_timeout (default "180")
How long to wait (in seconds) for a scheduled restart to begin if the cache
is not being accessed.
The Optimizer+ uses this directive to identify a situation where there may
be a problem with a process. After this time period has passed, the
Optimizer+ assumes that something has happened and starts killing the
processes that still hold the locks that are preventing a restart.
If the log level is 3 or above, a "killed locker" error is recorded
in the Apache logs when this happens.
zend_optimizerplus.error_log
Optimizer+ error_log file name. Empty string assumes "stderr"
zend_optimizerplus.log_verbosity_level (default "1")
Alll Optimizer+ errors go to the Web server log.
By default, only fatal errors (level 0) or errors (level 1) are logged.
You can also enable warnings (level 2), info messages (level 3) or
debug messesges (level 4).

2510
ZendAccelerator.c
File diff suppressed because it is too large
View File

364
ZendAccelerator.h

@ -0,0 +1,364 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_ACCELERATOR_H
#define ZEND_ACCELERATOR_H
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#define ACCELERATOR_PRODUCT_NAME "Zend Optimizer+"
#define ACCELERATOR_VERSION "7.0.0"
/* 2 - added Profiler support, on 20010712 */
/* 3 - added support for Optimizer's encoded-only-files mode */
/* 4 - works with the new Optimizer, that supports the file format with licenses */
/* 5 - API 4 didn't really work with the license-enabled file format. v5 does. */
/* 6 - Monitor was removed from ZendPlatform.so, to a module of its own */
/* 7 - Optimizer was embeded into Accelerator */
/* 8 - Standalone Open Source OptimizerPlus */
#define ACCELERATOR_API_NO 8
#if ZEND_WIN32
# include "zend_config.w32.h"
#else
#include "zend_config.h"
# include <sys/time.h>
# include <sys/resource.h>
#endif
#if HAVE_UNISTD_H
# include "unistd.h"
#endif
#include "zend_extensions.h"
#include "zend_compile.h"
#include "Optimizer/zend_optimizer.h"
#include "zend_accelerator_hash.h"
#include "zend_accelerator_debug.h"
#ifndef PHPAPI
# ifdef ZEND_WIN32
# define PHPAPI __declspec(dllimport)
# else
# define PHPAPI
# endif
#endif
#ifndef ZEND_EXT_API
# if WIN32|WINNT
# define ZEND_EXT_API __declspec(dllexport)
# else
# define ZEND_EXT_API
# endif
#endif
#ifdef ZEND_WIN32
# ifndef MAXPATHLEN
# define MAXPATHLEN _MAX_PATH
# endif
# include <direct.h>
#else
# include <sys/param.h>
#endif
#define PHP_5_0_X_API_NO 220040412
#define PHP_5_1_X_API_NO 220051025
#define PHP_5_2_X_API_NO 220060519
#define PHP_5_3_X_API_NO 220090626
#define PHP_5_4_X_API_NO 220100525
/*** file locking ***/
#ifndef ZEND_WIN32
extern int lock_file;
# if defined(__FreeBSD__) || (defined(__APPLE__) && defined(__MACH__)/* Darwin */) || defined(__OpenBSD__) || defined(__NetBSD__)
# define FLOCK_STRUCTURE(name, type, whence, start, len) \
struct flock name = {start, len, -1, type, whence}
# elif defined(__svr4__)
# define FLOCK_STRUCTURE(name, type, whence, start, len) \
struct flock name = {type, whence, start, len}
# elif defined(__linux__) || defined(__hpux)
# define FLOCK_STRUCTURE(name, type, whence, start, len) \
struct flock name = {type, whence, start, len, 0}
# elif defined(_AIX)
# if defined(_LARGE_FILES) || defined(__64BIT__)
# define FLOCK_STRUCTURE(name, type, whence, start, len) \
struct flock name = {type, whence, 0, 0, 0, start, len }
# else
# define FLOCK_STRUCTURE(name, type, whence, start, len) \
struct flock name = {type, whence, start, len}
# endif
# else
# error "Don't know how to define struct flock"
# endif
#endif
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
#define Z_REFCOUNT_P(pz) (pz)->refcount
#define Z_SET_REFCOUNT_P(pz, v) (pz)->refcount = (v)
#define Z_ADDREF_P(pz) ++((pz)->refcount)
#define Z_DELREF_P(pz) --((pz)->refcount)
#define Z_ISREF_P(pz) (pz)->is_ref
#define Z_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1)
#define Z_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0)
#define Z_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = (isref)
#define PZ_REFCOUNT_P(pz) (pz)->refcount
#define PZ_SET_REFCOUNT_P(pz, v) (pz)->refcount = (v)
#define PZ_ADDREF_P(pz) ++((pz)->refcount)
#define PZ_DELREF_P(pz) --((pz)->refcount)
#define PZ_ISREF_P(pz) (pz)->is_ref
#define PZ_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1)
#define PZ_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0)
#define PZ_SET_ISREF_TO_P(pz, isref) (pz)->is_ref = (isref)
#else
#define PZ_REFCOUNT_P(pz) (pz)->refcount__gc
#define PZ_SET_REFCOUNT_P(pz, v) (pz)->refcount__gc = (v)
#define PZ_ADDREF_P(pz) ++((pz)->refcount__gc)
#define PZ_DELREF_P(pz) --((pz)->refcount__gc)
#define PZ_ISREF_P(pz) (pz)->is_ref__gc
#define PZ_SET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 1)
#define PZ_UNSET_ISREF_P(pz) Z_SET_ISREF_TO_P(pz, 0)
#define PZ_SET_ISREF_TO_P(pz, isref) (pz)->is_ref__gc = (isref)
#endif
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
# ifdef ALLOCA_FLAG
#define DO_ALLOCA(x) do_alloca_with_limit(x,use_heap)
#define FREE_ALLOCA(x) free_alloca_with_limit(x,use_heap)
# else
#define ALLOCA_FLAG(x)
#define DO_ALLOCA(x) do_alloca(x)
#define FREE_ALLOCA(x) free_alloca(x)
# endif
#else
#define DO_ALLOCA(x) do_alloca(x,use_heap)
#define FREE_ALLOCA(x) free_alloca(x,use_heap)
#endif
#if ZEND_WIN32
typedef unsigned __int64 accel_time_t;
#else
typedef time_t accel_time_t;
#endif
typedef struct _zend_persistent_script {
ulong hash_value;
char *full_path; /* full real path with resolved symlinks */
int full_path_len;
zend_op_array main_op_array;
HashTable function_table;
HashTable class_table;
long compiler_halt_offset; /* position of __HALT_COMPILER or -1 */
int ping_auto_globals_mask; /* which autoglobals are used by the script */
accel_time_t timestamp; /* the script modification time */
zend_bool corrupted;
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
zend_uint early_binding; /* the linked list of delayed declarations */
#endif
void *mem; /* shared memory area used by script structures */
size_t size; /* size of used shared memory */
/* All entries that shouldn't be counted in the ADLER32
* checksum must be declared in this struct
*/
struct zend_persistent_script_dynamic_members {
time_t last_used;
ulong hits;
unsigned int memory_consumption;
unsigned int checksum;
time_t revalidate;
} dynamic_members;
} zend_persistent_script;
typedef struct _zend_accel_directives {
long memory_consumption;
long max_accelerated_files;
double max_wasted_percentage;
char *user_blacklist_filename;
long consistency_checks;
long force_restart_timeout;
zend_bool use_cwd;
zend_bool ignore_dups;
zend_bool validate_timestamps;
zend_bool revalidate_path;
zend_bool save_comments;
zend_bool fast_shutdown;
zend_bool protect_memory;
zend_bool file_override_enabled;
zend_bool inherited_hack;
zend_bool enable_cli;
unsigned long revalidate_freq;
char *error_log;
#ifdef ZEND_WIN32
char *mmap_base;
#endif
char *memory_model;
long log_verbosity_level;
long optimization_level;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
long interned_strings_buffer;
#endif
} zend_accel_directives;
typedef struct _zend_accel_globals {
/* copy of CG(function_table) used for compilation scripts into cashe */
/* imitially it contains only internal functions */
HashTable function_table;
int internal_functions_count;
int counted; /* the process uses shatred memory */
zend_bool enabled;
HashTable bind_hash; /* prototype and zval lookup table */
zend_accel_directives accel_directives;
char *cwd; /* current working directory or NULL */
int cwd_len; /* "cwd" string lenght */
char *include_path_key; /* one letter key of current "include_path" */
char *include_path; /* current settion of "include_path" directive */
int include_path_len; /* "include_path" string lenght */
int include_path_check;
time_t request_time;
zend_bool startup_ok;
/* preallocated shared-memory block to save current script */
void *mem;
/* cache to save hash lookup on the same INCLUDE opcode */
zend_op *cache_opline;
zend_persistent_script *cache_persistent_script;
/* preallocated buffer for keys */
int key_len;
char key[MAXPATHLEN * 8];
} zend_accel_globals;
typedef struct _zend_accel_shared_globals {
/* Cache Data Structures */
unsigned long hits;
unsigned long misses;
unsigned long blacklist_misses;
zend_accel_hash hash; /* hash table for cached scripts */
zend_accel_hash include_paths; /* used "include_path" values */
/* Directives & Maintenance */
time_t last_restart_time;
time_t force_restart_time;
zend_bool accelerator_enabled;
zend_bool restart_pending;
zend_bool cache_status_before_restart;
#ifdef ZEND_WIN32
unsigned long mem_usage;
unsigned long restart_in;
#endif
zend_bool restart_in_progress;
time_t revalidate_at;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
/* Interned Strings Support */
char *interned_strings_start;
char *interned_strings_top;
char *interned_strings_end;
HashTable interned_strings;
struct {
Bucket **arBuckets;
Bucket *pListHead;
Bucket *pListTail;
char *top;
} interned_strings_saved_state;
#endif
} zend_accel_shared_globals;
extern zend_accel_shared_globals *accel_shared_globals;
#define ZCSG(element) (accel_shared_globals->element)
#ifdef ZTS
# define ZCG(v) TSRMG(accel_globals_id, zend_accel_globals *, v)
extern int accel_globals_id;
#else
# define ZCG(v) (accel_globals.v)
extern zend_accel_globals accel_globals;
#endif
extern char *zps_api_failure_reason;
void zend_accel_schedule_restart(TSRMLS_D);
int accelerator_shm_read_lock(TSRMLS_D);
void accelerator_shm_read_unlock(TSRMLS_D);
char *accel_make_persistent_key_ex(zend_file_handle *file_handle, int path_length, int *key_len TSRMLS_DC);
#if !defined(ZEND_DECLARE_INHERITED_CLASS_DELAYED)
# define ZEND_DECLARE_INHERITED_CLASS_DELAYED 145
#endif
#define ZEND_DECLARE_INHERITED_CLASS_DELAYED_FLAG 0x80
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
const char *accel_new_interned_string(const char *arKey, int nKeyLength, int free_src TSRMLS_DC);
# define interned_free(s) do { \
if (!IS_INTERNED(s)) { \
free(s); \
} \
} while (0)
# define interned_efree(s) do { \
if (!IS_INTERNED(s)) { \
efree(s); \
} \
} while (0)
# define interned_estrndup(s, n) \
(IS_INTERNED(s) ? (s) : estrndup(s, n))
# define ZEND_RESULT_TYPE(opline) (opline)->result_type
# define ZEND_RESULT(opline) (opline)->result
# define ZEND_OP1_TYPE(opline) (opline)->op1_type
# define ZEND_OP1(opline) (opline)->op1
# define ZEND_OP1_CONST(opline) (*(opline)->op1.zv)
# define ZEND_OP1_LITERAL(opline) (op_array)->literals[(opline)->op1.constant].constant
# define ZEND_OP2_TYPE(opline) (opline)->op2_type
# define ZEND_OP2(opline) (opline)->op2
# define ZEND_OP2_CONST(opline) (*(opline)->op2.zv)
# define ZEND_OP2_LITERAL(opline) (op_array)->literals[(opline)->op2.constant].constant
# define ZEND_DONE_PASS_TWO(op_array) (((op_array)->fn_flags & ZEND_ACC_DONE_PASS_TWO) != 0)
# define ZEND_CE_FILENAME(ce) (ce)->info.user.filename
# define ZEND_CE_DOC_COMMENT(ce) (ce)->info.user.doc_comment
# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->info.user.doc_comment_len
#else
# define IS_INTERNED(s) 0
# define interned_free(s) free(s)
# define interned_efree(s) efree(s)
# define interned_estrndup(s, n) estrndup(s, n)
# define ZEND_RESULT_TYPE(opline) (opline)->result.op_type
# define ZEND_RESULT(opline) (opline)->result.u
# define ZEND_OP1_TYPE(opline) (opline)->op1.op_type
# define ZEND_OP1(opline) (opline)->op1.u
# define ZEND_OP1_CONST(opline) (opline)->op1.u.constant
# define ZEND_OP1_LITERAL(opline) (opline)->op1.u.constant
# define ZEND_OP2_TYPE(opline) (opline)->op2.op_type
# define ZEND_OP2(opline) (opline)->op2.u
# define ZEND_OP2_CONST(opline) (opline)->op2.u.constant
# define ZEND_OP2_LITERAL(opline) (opline)->op2.u.constant
# define ZEND_DONE_PASS_TWO(op_array) ((op_array)->done_pass_two != 0)
# define ZEND_CE_FILENAME(ce) (ce)->filename
# define ZEND_CE_DOC_COMMENT(ce) (ce)->doc_comment
# define ZEND_CE_DOC_COMMENT_LEN(ce) (ce)->doc_comment_len
#endif
#endif /* ZEND_ACCELERATOR_H */

345
config.m4

@ -0,0 +1,345 @@
dnl
dnl $Id$
dnl
PHP_ARG_ENABLE(optimizer-plus, whether to enable Zend OptimizerPlus support,
[ --enable-optimizer-plus Enable Zend OptimizerPlus support])
if test "$PHP_OPTIMIZER_PLUS" != "no"; then
AC_DEFINE(HAVE_OPTIMIZER_PLUS, 1, [ ])
AC_CHECK_FUNC(mprotect,[
AC_DEFINE(HAVE_MPROTECT, 1, [Define if you have mprotect() function])
])
AC_MSG_CHECKING(for sysvipc shared memory support)
AC_TRY_RUN([
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <string.h>
int main() {
pid_t pid;
int status;
int ipc_id;
char *shm;
struct shmid_ds shmbuf;
ipc_id = shmget(IPC_PRIVATE, 4096, (IPC_CREAT | SHM_R | SHM_W));
if (ipc_id == -1) {
return 1;
}
shm = shmat(ipc_id, NULL, 0);
if (shm == (void *)-1) {
shmctl(ipc_id, IPC_RMID, NULL);
return 2;
}
if (shmctl(ipc_id, IPC_STAT, &shmbuf) != 0) {
shmdt(shm);
shmctl(ipc_id, IPC_RMID, NULL);
return 3;
}
shmbuf.shm_perm.uid = getuid();
shmbuf.shm_perm.gid = getgid();
shmbuf.shm_perm.mode = 0600;
if (shmctl(ipc_id, IPC_SET, &shmbuf) != 0) {
shmdt(shm);
shmctl(ipc_id, IPC_RMID, NULL);
return 4;
}
shmctl(ipc_id, IPC_RMID, NULL);
strcpy(shm, "hello");
pid = fork();
if (pid < 0) {
return 5;
} else if (pid == 0) {
strcpy(shm, "bye");
return 6;
}
if (wait(&status) != pid) {
return 7;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
return 8;
}
if (strcmp(shm, "bye") != 0) {
return 9;
}
return 0;
}
],dnl
AC_DEFINE(HAVE_SHM_IPC, 1, [Define if you have SysV IPC SHM support])
msg=yes,msg=no,msg=no)
AC_MSG_RESULT([$msg])
AC_MSG_CHECKING(for mmap() using MAP_ANON shared memory support)
AC_TRY_RUN([
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#ifndef MAP_ANON
# ifdef MAP_ANONYMOUS
# define MAP_ANON MAP_ANONYMOUS
# endif
#endif
#ifndef MAP_FAILED
# define MAP_FAILED ((void*)-1)
#endif
int main() {
pid_t pid;
int status;
char *shm;
shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
if (shm == MAP_FAILED) {
return 1;
}
strcpy(shm, "hello");
pid = fork();
if (pid < 0) {
return 5;
} else if (pid == 0) {
strcpy(shm, "bye");
return 6;
}
if (wait(&status) != pid) {
return 7;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
return 8;
}
if (strcmp(shm, "bye") != 0) {
return 9;
}
return 0;
}
],dnl
AC_DEFINE(HAVE_SHM_MMAP_ANON, 1, [Define if you have mmap(MAP_ANON) SHM support])
msg=yes,msg=no,msg=no)
AC_MSG_RESULT([$msg])
AC_MSG_CHECKING(for mmap() using /dev/zero shared memory support)
AC_TRY_RUN([
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#ifndef MAP_FAILED
# define MAP_FAILED ((void*)-1)
#endif
int main() {
pid_t pid;
int status;
int fd;
char *shm;
fd = open("/dev/zero", O_RDWR, S_IRUSR | S_IWUSR);
if (fd == -1) {
return 1;
}
shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shm == MAP_FAILED) {
return 2;
}
strcpy(shm, "hello");
pid = fork();
if (pid < 0) {
return 5;
} else if (pid == 0) {
strcpy(shm, "bye");
return 6;
}
if (wait(&status) != pid) {
return 7;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
return 8;
}
if (strcmp(shm, "bye") != 0) {
return 9;
}
return 0;
}
],dnl
AC_DEFINE(HAVE_SHM_MMAP_ZERO, 1, [Define if you have mmap("/dev/zero") SHM support])
msg=yes,msg=no,msg=no)
AC_MSG_RESULT([$msg])
AC_MSG_CHECKING(for mmap() using shm_open() shared memory support)
AC_TRY_RUN([
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef MAP_FAILED
# define MAP_FAILED ((void*)-1)
#endif
int main() {
pid_t pid;
int status;
int fd;
char *shm;
char tmpname[4096];
sprintf(tmpname,"test.shm.%dXXXXXX", getpid());
if (mktemp(tmpname) == NULL) {
return 1;
}
fd = shm_open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
return 2;
}
if (ftruncate(fd, 4096) < 0) {
close(fd);
shm_unlink(tmpname);
return 3;
}
shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shm == MAP_FAILED) {
return 4;
}
shm_unlink(tmpname);
close(fd);
strcpy(shm, "hello");
pid = fork();
if (pid < 0) {
return 5;
} else if (pid == 0) {
strcpy(shm, "bye");
return 6;
}
if (wait(&status) != pid) {
return 7;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
return 8;
}
if (strcmp(shm, "bye") != 0) {
return 9;
}
return 0;
}
],dnl
AC_DEFINE(HAVE_SHM_MMAP_POSIX, 1, [Define if you have POSIX mmap() SHM support])
msg=yes,msg=no,msg=no)
AC_MSG_RESULT([$msg])
AC_MSG_CHECKING(for mmap() using regular file shared memory support)
AC_TRY_RUN([
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef MAP_FAILED
# define MAP_FAILED ((void*)-1)
#endif
int main() {
pid_t pid;
int status;
int fd;
char *shm;
char tmpname[4096];
sprintf(tmpname,"test.shm.%dXXXXXX", getpid());
if (mktemp(tmpname) == NULL) {
return 1;
}
fd = open(tmpname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
return 2;
}
if (ftruncate(fd, 4096) < 0) {
close(fd);
unlink(tmpname);
return 3;
}
shm = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (shm == MAP_FAILED) {
return 4;
}
unlink(tmpname);
close(fd);
strcpy(shm, "hello");
pid = fork();
if (pid < 0) {
return 5;
} else if (pid == 0) {
strcpy(shm, "bye");
return 6;
}
if (wait(&status) != pid) {
return 7;
}
if (!WIFEXITED(status) || WEXITSTATUS(status) != 6) {
return 8;
}
if (strcmp(shm, "bye") != 0) {
return 9;
}
return 0;
}
],dnl
AC_DEFINE(HAVE_SHM_MMAP_FILE, 1, [Define if you have mmap() SHM support])
msg=yes,msg=no,msg=no)
AC_MSG_RESULT([$msg])
PHP_NEW_EXTENSION(ZendOptimizerPlus,
ZendAccelerator.c \
zend_accelerator_blacklist.c \
zend_accelerator_debug.c \
zend_accelerator_hash.c \
zend_accelerator_module.c \
zend_persist.c \
zend_persist_calc.c \
zend_shared_alloc.c \
zend_accelerator_util_funcs.c \
shared_alloc_shm.c \
shared_alloc_mmap.c \
shared_alloc_posix.c \
Optimizer/zend_optimizer.c,
$ext_shared)
fi

28
config.w32

@ -0,0 +1,28 @@
ARG_ENABLE("optimizer-plus", "whether to enable Zend OptimizerPlus support", "yes");
if (PHP_OPTIMIZER_PLUS != "no") {
PHP_PGI = "no"; // workaround
PHP_PGO = "no"; // workaround
EXTENSION('ZendOptimizerPlus', "\
ZendAccelerator.c \
zend_accelerator_blacklist.c \
zend_accelerator_debug.c \
zend_accelerator_hash.c \
zend_accelerator_module.c \
zend_accelerator_util_funcs.c \
zend_persist.c \
zend_persist_calc.c \
zend_shared_alloc.c \
shared_alloc_win32.c", true);
ADD_SOURCES("Optimizer", "zend_optimizer.c", "ZendOptimizerPlus", "OptimizerObj");
ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/I .");
ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/D HAVE_OPTIMIZER_PLUS=1");
ADD_FLAG('CFLAGS_ZENDOPTIMIZERPLUS', "/Dregexec=php_regexec /Dregerror=php_regerror /Dregfree=php_regfree /Dregcomp=php_regcomp /Iext/ereg/regex");
}

70
shared_alloc_mmap.c

@ -0,0 +1,70 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include "zend_shared_alloc.h"
#if defined(MAP_ANON) && !defined(MAP_ANONYMOUS)
# define MAP_ANONYMOUS MAP_ANON
#endif
static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
{
zend_shared_segment *shared_segment;
*shared_segments_count = 1;
*shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *));
shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *));
(*shared_segments_p)[0] = shared_segment;
shared_segment->p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
if(shared_segment->p == MAP_FAILED) {
*error_in = "mmap";
return ALLOC_FAILURE;
}
shared_segment->pos = 0;
shared_segment->size = requested_size;
return ALLOC_SUCCESS;
}
static int detach_segment(zend_shared_segment *shared_segment)
{
munmap(shared_segment->p, shared_segment->size);
return 0;
}
static size_t segment_type_size(void)
{
return sizeof(zend_shared_segment);
}
zend_shared_memory_handlers zend_alloc_mmap_handlers = {
create_segments,
detach_segment,
segment_type_size
};

90
shared_alloc_posix.c

@ -0,0 +1,90 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdlib.h>
#include "zend_shared_alloc.h"
typedef struct {
zend_shared_segment common;
int shm_fd;
} zend_shared_segment_posix;
static int create_segments(size_t requested_size, zend_shared_segment_posix ***shared_segments_p, int *shared_segments_count, char **error_in)
{
zend_shared_segment_posix *shared_segment;
char shared_segment_name[sizeof("/ZendAccelerator.")+20];
*shared_segments_count = 1;
*shared_segments_p = (zend_shared_segment_posix **) calloc(1, sizeof(zend_shared_segment_posix)+sizeof(void *));
shared_segment = (zend_shared_segment_posix *)((char *)(*shared_segments_p) + sizeof(void *));
(*shared_segments_p)[0] = shared_segment;
sprintf(shared_segment_name, "/ZendAccelerator.%d", getpid());
shared_segment->shm_fd = shm_open(shared_segment_name, O_RDWR|O_CREAT|O_TRUNC, 0600);
if(shared_segment->shm_fd == -1) {
*error_in = "shm_open";
return ALLOC_FAILURE;
}
if(ftruncate(shared_segment->shm_fd, requested_size) != 0) {
*error_in = "ftruncate";
shm_unlink(shared_segment_name);
return ALLOC_FAILURE;
}
shared_segment->common.p = mmap(0, requested_size, PROT_READ | PROT_WRITE, MAP_SHARED, shared_segment->shm_fd, 0);
if(shared_segment->common.p == MAP_FAILED) {
*error_in = "mmap";
shm_unlink(shared_segment_name);
return ALLOC_FAILURE;
}
shm_unlink(shared_segment_name);
shared_segment->common.pos = 0;
shared_segment->common.size = requested_size;
return ALLOC_SUCCESS;
}
static int detach_segment(zend_shared_segment_posix *shared_segment)
{
munmap(shared_segment->common.p, shared_segment->common.size);
close(shared_segment->shm_fd);
return 0;
}
static size_t segment_type_size(void)
{
return sizeof(zend_shared_segment_posix);
}
zend_shared_memory_handlers zend_alloc_posix_handlers = {
(create_segments_t)create_segments,
(detach_segment_t)detach_segment,
segment_type_size
};

137
shared_alloc_shm.c

@ -0,0 +1,137 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#if defined(__FreeBSD__)
# include <machine/param.h>
#endif
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/ipc.h>
#include <dirent.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "zend_shared_alloc.h"
#ifndef MIN
# define MIN(x, y) ((x) > (y)? (y) : (x))
#endif
#define SEG_ALLOC_SIZE_MAX 32*1024*1024
#define SEG_ALLOC_SIZE_MIN 2*1024*1024
typedef struct {
zend_shared_segment common;
int shm_id;
} zend_shared_segment_shm;
static int create_segments(size_t requested_size, zend_shared_segment_shm ***shared_segments_p, int *shared_segments_count, char **error_in)
{
int i;
unsigned int allocate_size=0, remaining_bytes=requested_size, seg_allocate_size;
int first_segment_id=-1;
key_t first_segment_key = -1;
struct shmid_ds sds;
int shmget_flags;
zend_shared_segment_shm *shared_segments;
seg_allocate_size = SEG_ALLOC_SIZE_MAX;
/* determine segment size we _really_ need:
* no more than to include requested_size
*/
while (requested_size*2 <= seg_allocate_size && seg_allocate_size > SEG_ALLOC_SIZE_MIN) {
seg_allocate_size >>= 1;
}
shmget_flags = IPC_CREAT|SHM_R|SHM_W|IPC_EXCL;
/* try allocating this much, if not - try shrinking */
while (seg_allocate_size >= SEG_ALLOC_SIZE_MIN) {
allocate_size = MIN(requested_size, seg_allocate_size);
first_segment_id = shmget(first_segment_key, allocate_size, shmget_flags);
if (first_segment_id != -1) {
break;
}
seg_allocate_size >>= 1; /* shrink the allocated block */
}
if (first_segment_id == -1) {
*error_in = "shmget";
return ALLOC_FAILURE;
}
*shared_segments_count = ((requested_size-1)/seg_allocate_size) + 1;
*shared_segments_p = (zend_shared_segment_shm **) calloc(1, (*shared_segments_count)*sizeof(zend_shared_segment_shm)+sizeof(void *)*(*shared_segments_count));
shared_segments = (zend_shared_segment_shm *)((char *)(*shared_segments_p) + sizeof(void *)*(*shared_segments_count));
for(i=0; i<*shared_segments_count; i++) {
(*shared_segments_p)[i] = shared_segments+i;
}
remaining_bytes = requested_size;
for (i=0; i<*shared_segments_count; i++) {
allocate_size = MIN(remaining_bytes, seg_allocate_size);
if (i != 0) {
shared_segments[i].shm_id = shmget(IPC_PRIVATE, allocate_size, shmget_flags);
} else {
shared_segments[i].shm_id = first_segment_id;
}
if (shared_segments[i].shm_id==-1) {
return ALLOC_FAILURE;
}
shared_segments[i].common.p = shmat(shared_segments[i].shm_id, NULL, 0);
if (((int) shared_segments[i].common.p) == -1) {
*error_in = "shmat";
shmctl(shared_segments[i].shm_id, IPC_RMID, &sds);
return ALLOC_FAILURE;
}
shmctl(shared_segments[i].shm_id, IPC_RMID, &sds);
shared_segments[i].common.pos = 0;
shared_segments[i].common.size = allocate_size;
remaining_bytes -= allocate_size;
}
return ALLOC_SUCCESS;
}
static int detach_segment(zend_shared_segment_shm *shared_segment)
{
shmdt(shared_segment->common.p);
return 0;
}
static size_t segment_type_size(void)
{
return sizeof(zend_shared_segment_shm);
}
zend_shared_memory_handlers zend_alloc_shm_handlers = {
(create_segments_t)create_segments,
(detach_segment_t)detach_segment,
segment_type_size
};

315
shared_alloc_win32.c

@ -0,0 +1,315 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include "ZendAccelerator.h"
#include "zend_shared_alloc.h"
#include "zend_accelerator_util_funcs.h"
#include <winbase.h>
#include <process.h>
#include <LMCONS.H>
#define ACCEL_FILEMAP_NAME "ZendOptimizer+.SharedMemoryArea"
#define ACCEL_MUTEX_NAME "ZendOptimizer+.SharedMemoryMutex"
#define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000
#define ACCEL_FILEMAP_BASE "ZendOptimizer+.MemoryBase"
#define ACCEL_EVENT_SOURCE "Zend Optimizer+"
static HANDLE memfile = NULL, memory_mutex = NULL;
static void *mapping_base;
#define MAX_MAP_RETRIES 25
static void zend_win_error_message(int type, char *msg, int err)
{
LPVOID lpMsgBuf;
FILE *fp;
HANDLE h;
char *ev_msgs[2];
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
h = RegisterEventSource(NULL, TEXT(ACCEL_EVENT_SOURCE));
ev_msgs[0] = msg;
ev_msgs[1] = lpMsgBuf;
ReportEvent(h, // event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // category zero
err, // event identifier
NULL, // no user security identifier
2, // one substitution string
0, // no data
ev_msgs, // pointer to string array
NULL); // pointer to data
DeregisterEventSource(h);
LocalFree( lpMsgBuf );
zend_accel_error(type, msg);
}
static char *create_name_with_username(char *name)
{
static char newname[MAXPATHLEN+UNLEN+4];
char uname[UNLEN+1];
DWORD unsize = UNLEN;
GetUserName(uname, &unsize);
snprintf(newname, sizeof(newname)-1, "%s@%s", name, uname);
return newname;
}
static char *get_mmap_base_file()
{
static char windir[MAXPATHLEN+UNLEN+3+sizeof("\\\\@")];
char uname[UNLEN+1];
DWORD unsize = UNLEN;
int l;
GetTempPath(MAXPATHLEN, windir);
GetUserName(uname, &unsize);
l = strlen(windir);
snprintf(windir+l, sizeof(windir)-l-1, "\\%s@%s", ACCEL_FILEMAP_BASE, uname);
return windir;
}
void zend_shared_alloc_create_lock(void)
{
memory_mutex = CreateMutex(NULL, FALSE, create_name_with_username(ACCEL_MUTEX_NAME));
ReleaseMutex(memory_mutex);
}
void zend_shared_alloc_lock_win32()
{
DWORD waitRes = WaitForSingleObject(memory_mutex, INFINITE);
if(waitRes == WAIT_FAILED) {
zend_accel_error(ACCEL_LOG_ERROR, "Cannot lock mutex");
}
}
void zend_shared_alloc_unlock_win32(TSRMLS_D)
{
ReleaseMutex(memory_mutex);
}
static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
{
int err;
void *wanted_mapping_base;
char *mmap_base_file = get_mmap_base_file();
FILE *fp = fopen(mmap_base_file, "r");
MEMORY_BASIC_INFORMATION info;
err = GetLastError();
if(!fp) {
zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open base address file", err);
*error_in="fopen";
return ALLOC_FAILURE;
}
if(!fscanf(fp, "%p", &wanted_mapping_base)) {
err = GetLastError();
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read base address", err);
*error_in="read mapping base";
return ALLOC_FAILURE;
}
fclose(fp);
/* Check if the requested address space is free */
if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
info.State != MEM_FREE ||
info.RegionSize < requested_size) {
err = ERROR_INVALID_ADDRESS;
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err);
return ALLOC_FAILURE;
}
mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base);
err = GetLastError();
if(mapping_base == NULL) {
if (err == ERROR_INVALID_ADDRESS) {
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err);
return ALLOC_FAILURE;
}
return ALLOC_FAIL_MAPPING;
}
smm_shared_globals = (zend_smm_shared_globals *) (((char *) mapping_base) + sizeof(zend_shared_memory_block_header));
return SUCCESSFULLY_REATTACHED;
}
static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
{
int err, ret;
zend_shared_segment *shared_segment;
int map_retries = 0;
void *default_mapping_base_set[] = { 0, 0 };
void *vista_mapping_base_set[] = { (void *)0x20000000, (void *)0x21000000, (void *)0x30000000, (void *)0x31000000, (void *)0x50000000, 0 };
void **wanted_mapping_base = default_mapping_base_set;
TSRMLS_FETCH();
/* Mapping retries: When Apache2 restarts, the parent process startup routine
can be called before the child process is killed. In this case, the map will fail
and we have to sleep some time (until the child releases the mapping object) and retry.*/
do {
memfile = OpenFileMapping(FILE_MAP_WRITE, 0, create_name_with_username(ACCEL_FILEMAP_NAME));
err = GetLastError();
if (memfile == NULL)
break;
ret = zend_shared_alloc_reattach(requested_size, error_in);
err = GetLastError();
if (ret == ALLOC_FAIL_MAPPING) {
/* Mapping failed, wait for mapping object to get freed and retry */
CloseHandle(memfile);
memfile = NULL;
Sleep(1000*(map_retries+1));
} else {
return ret;
}
} while(++map_retries < MAX_MAP_RETRIES);
if(map_retries == MAX_MAP_RETRIES) {
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open file mapping", err);
*error_in = "OpenFileMapping";
return ALLOC_FAILURE;
}
/* creating segment here */
*shared_segments_count = 1;
*shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *));
shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *));
(*shared_segments_p)[0] = shared_segment;
memfile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, requested_size,
create_name_with_username(ACCEL_FILEMAP_NAME));
err = GetLastError();
if(memfile == NULL) {
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create file mapping", err);
*error_in = "CreateFileMapping";
return ALLOC_FAILURE;
}
/* Starting from windows Vista, heap randomization occurs which might cause our mapping base to
be taken (fail to map). So under Vista, we try to map into a hard coded predefined addresses
in high memory. */
if (!ZCG(accel_directives).mmap_base || !*ZCG(accel_directives).mmap_base) {
do {
OSVERSIONINFOEX osvi;
SYSTEM_INFO si;
ZeroMemory(&si, sizeof(SYSTEM_INFO));
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
if (! GetVersionEx ((OSVERSIONINFO *) &osvi)) {
osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) )
break;
}
GetSystemInfo(&si);
/* Are we running Vista ? */
if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion == 6 ) {
/* Assert that platform is 32 bit (for 64 bit we need to test a different set */
if(si.wProcessorArchitecture != PROCESSOR_ARCHITECTURE_INTEL)
DebugBreak();
wanted_mapping_base = vista_mapping_base_set;
}
} while (0);
} else {
char *s = ZCG(accel_directives).mmap_base;
/* skip leading 0x, %p assumes hexdeciaml format anyway */
if (*s == '0' && *(s+1) == 'x') {
s += 2;
}
if (sscanf(s, "%p", &default_mapping_base_set[0]) != 1) {
zend_win_error_message(ACCEL_LOG_FATAL, "Bad mapping address specified in zend_optimizerplus.mmap_base", err);
return ALLOC_FAILURE;
}
}
do {
shared_segment->p = mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, *wanted_mapping_base);
if(*wanted_mapping_base == NULL) /* Auto address (NULL) is the last option on the array */
break;
wanted_mapping_base++;
} while (!mapping_base);
err = GetLastError();
if(mapping_base == NULL) {
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create view for file mapping", err);
*error_in = "MapViewOfFile";
return ALLOC_FAILURE;
} else {
char *mmap_base_file = get_mmap_base_file();
FILE *fp = fopen(mmap_base_file, "w");
err = GetLastError();
if(!fp) {
zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
zend_win_error_message(ACCEL_LOG_FATAL, "Unable to write base address", err);
}
fprintf(fp, "%p\n", mapping_base);
fclose(fp);
}
shared_segment->pos = 0;
shared_segment->size = requested_size;
return ALLOC_SUCCESS;
}
static int detach_segment(zend_shared_segment *shared_segment)
{
if(mapping_base) {
UnmapViewOfFile(mapping_base);
}
CloseHandle(memfile);
ReleaseMutex(memory_mutex);
CloseHandle(memory_mutex);
return 0;
}
static size_t segment_type_size(void)
{
return sizeof(zend_shared_segment);
}
zend_shared_memory_handlers zend_alloc_win32_handlers = {
create_segments,
detach_segment,
segment_type_size
};

241
zend_accelerator_blacklist.c

@ -0,0 +1,241 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include "main/php.h"
#include "main/fopen_wrappers.h"
#include "ZendAccelerator.h"
#include "zend_accelerator_blacklist.h"
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
# include "ext/ereg/php_regex.h"
#else
# include "main/php_regex.h"
#endif
#ifdef ZEND_WIN32
# define REGEX_MODE (REG_EXTENDED|REG_NOSUB|REG_ICASE)
#else
# define REGEX_MODE (REG_EXTENDED|REG_NOSUB)
#endif
#define ZEND_BLACKLIST_BLOCK_SIZE 32
struct _zend_regexp_list {
regex_t comp_regex;
zend_regexp_list *next;
};
zend_blacklist accel_blacklist;
void zend_accel_blacklist_init(zend_blacklist *blacklist)
{
blacklist->pos = 0;
blacklist->size = ZEND_BLACKLIST_BLOCK_SIZE;
if( blacklist->entries != NULL ){
zend_accel_blacklist_shutdown(blacklist);
}
blacklist->entries = (zend_blacklist_entry *) calloc(sizeof(zend_blacklist_entry), blacklist->size);
blacklist->regexp_list = NULL;
}
static void blacklist_report_regexp_error(regex_t *comp_regex, int reg_err)
{
char *errbuf;
int errsize = regerror(reg_err, comp_regex, NULL, 0);
errbuf = malloc(errsize);
regerror(reg_err, comp_regex, errbuf, errsize);
zend_accel_error(ACCEL_LOG_ERROR, "Blacklist compilation: %s\n", errbuf);
free(errbuf);
}
static void zend_accel_blacklist_update_regexp(zend_blacklist *blacklist)
{
int i, end=0, j, rlen=6, clen, reg_err;
char *regexp;
zend_regexp_list **regexp_list_it;
if (blacklist->pos == 0) {
/* we have no blacklist to talk about */
return;
}
regexp_list_it = &(blacklist->regexp_list);
for (i=0; i<blacklist->pos; i++) {
rlen += blacklist->entries[i].path_length*2+2;
/* don't create a regexp buffer bigger than 12K)*/
if((i+1 == blacklist->pos) || ((rlen+blacklist->entries[i+1].path_length*2+2)>(12*1024) ) ) {
regexp = (char *)malloc(rlen);
regexp[0] = '^';
regexp[1] = '(';
clen=2;
for (j=end; j<=i ;j++) {
int c;
if (j!=end) {
regexp[clen++] = '|';
}
/* copy mangled filename */
for(c=0; c<blacklist->entries[j].path_length; c++) {
if(strchr("^.[]$()|*+?{}\\", blacklist->entries[j].path[c])) {
regexp[clen++] = '\\';
}
regexp[clen++] = blacklist->entries[j].path[c];
}
}
regexp[clen++] = ')';
regexp[clen] = '\0';
(*regexp_list_it) = malloc(sizeof(zend_regexp_list));
(*regexp_list_it)->next = NULL;
if ((reg_err = regcomp(&((*regexp_list_it)->comp_regex), regexp, REGEX_MODE)) != 0) {
blacklist_report_regexp_error(&((*regexp_list_it)->comp_regex), reg_err);
}
/* prepare for the next iteration */
free(regexp);
end = i+1;
rlen = 6;
regexp_list_it = &((*regexp_list_it)->next);
}
}
}
void zend_accel_blacklist_shutdown(zend_blacklist *blacklist)
{
zend_blacklist_entry *p = blacklist->entries, *end = blacklist->entries + blacklist->pos;
while (p<end) {
free(p->path);
p++;
}
free(blacklist->entries);
blacklist->entries = NULL;
if (blacklist->regexp_list) {
zend_regexp_list *temp, *it = blacklist->regexp_list;
while( it ){
regfree(&it->comp_regex);
temp = it;
it = it->next;
free(temp);
}
}
}
static inline void zend_accel_blacklist_allocate(zend_blacklist *blacklist)
{
if (blacklist->pos==blacklist->size) {
blacklist->size += ZEND_BLACKLIST_BLOCK_SIZE;
blacklist->entries = (zend_blacklist_entry *) realloc(blacklist->entries, sizeof(zend_blacklist_entry)*blacklist->size);
}
}
void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename)
{
char buf[MAXPATHLEN+1], real_path[MAXPATHLEN+1];
FILE *fp;
int path_length;
TSRMLS_FETCH();
if ((fp=fopen(filename, "r"))==NULL) {
zend_accel_error(ACCEL_LOG_WARNING, "Cannot load blacklist file: %s\n", filename);
return;
}
zend_accel_error(ACCEL_LOG_DEBUG,"Loading blacklist file: '%s'", filename);
memset(buf, 0, sizeof(buf));
memset(real_path, 0, sizeof(real_path));
while (fgets(buf, MAXPATHLEN, fp)!=NULL) {
char *path_dup, *pbuf;
path_length = strlen(buf);
if (buf[path_length-1]=='\n') {
buf[--path_length] = 0;
if (buf[path_length-1]=='\r') {
buf[--path_length] = 0;
}
}
/* Strip ctrl-m prefix */
pbuf = &buf[0];
while(*pbuf == '\r') {
*pbuf++ = 0;
path_length--;
}
/* strip \" */
if( pbuf[0] == '\"' && pbuf[path_length-1]== '\"' ){
*pbuf++ = 0;
path_length-=2;
}
if (path_length==0) {
continue;
}
path_dup = zend_strndup(pbuf, path_length);
expand_filepath(path_dup, real_path TSRMLS_CC);
path_length = strlen(real_path);
free(path_dup);
zend_accel_blacklist_allocate(blacklist);
blacklist->entries[blacklist->pos].path_length = path_length;
blacklist->entries[blacklist->pos].path = (char *) malloc(path_length+1);
blacklist->entries[blacklist->pos].id = blacklist->pos;
memcpy(blacklist->entries[blacklist->pos].path, real_path, path_length+1);
blacklist->pos++;
}
fclose(fp);
zend_accel_blacklist_update_regexp(blacklist);
}
zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path)
{
int ret = 0;
zend_regexp_list *regexp_list_it = blacklist->regexp_list;
if (regexp_list_it == NULL) {
return 0;
}
while (regexp_list_it != NULL) {
if (regexec(&(regexp_list_it->comp_regex), verify_path, 0, NULL, 0) == 0) {
ret = 1;
break;
}
regexp_list_it = regexp_list_it->next;
}
return ret;
}
void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC)
{
int i;
for(i=0; i<blacklist->pos; i++) {
func(&blacklist->entries[i], argument TSRMLS_CC);
}
}

49
zend_accelerator_blacklist.h

@ -0,0 +1,49 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_ACCELERATOR_BLACKLIST_H
#define ZEND_ACCELERATOR_BLACKLIST_H
typedef struct _zend_regexp_list zend_regexp_list;
typedef struct _zend_blacklist_entry {
char *path;
int path_length;
int id;
} zend_blacklist_entry;
typedef struct _zend_blacklist {
zend_blacklist_entry *entries;
int size;
int pos;
zend_regexp_list *regexp_list;
} zend_blacklist;
extern zend_blacklist accel_blacklist;
void zend_accel_blacklist_init(zend_blacklist *blacklist);
void zend_accel_blacklist_shutdown(zend_blacklist *blacklist);
void zend_accel_blacklist_load(zend_blacklist *blacklist, char *filename);
zend_bool zend_accel_blacklist_is_blacklisted(zend_blacklist *blacklist, char *verify_path);
void zend_accel_blacklist_apply(zend_blacklist *blacklist, apply_func_arg_t func, void *argument TSRMLS_DC);
#endif /* ZEND_ACCELERATOR_BLACKLIST_H */

101
zend_accelerator_debug.c

@ -0,0 +1,101 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#ifdef ZEND_WIN32
# include <process.h>
#endif
#include "ZendAccelerator.h"
void zend_accel_error(int type, const char *format, ...)
{
va_list args;
time_t timestamp;
char *time_string;
FILE * fLog = NULL;
TSRMLS_FETCH();
if (type > ZCG(accel_directives).log_verbosity_level) {
return;
}
timestamp = time(NULL);
time_string = asctime(localtime(&timestamp));
time_string[24] = 0;
if (!ZCG(accel_directives).error_log ||
!*ZCG(accel_directives).error_log ||
strcmp(ZCG(accel_directives).error_log, "stderr") == 0) {
fLog = stderr;
} else {
fLog = fopen(ZCG(accel_directives).error_log, "a+");
if (!fLog) {
fLog = stderr;
}
}
fprintf(fLog, "%s (%d): ", time_string,
#ifdef ZTS
tsrm_thread_id()
#else
getpid()
#endif
);
switch (type) {
case ACCEL_LOG_FATAL:
fprintf(fLog, "Fatal Error ");
break;
case ACCEL_LOG_ERROR:
fprintf(fLog, "Error ");
break;
case ACCEL_LOG_WARNING:
fprintf(fLog, "Warning ");
break;
case ACCEL_LOG_INFO:
fprintf(fLog, "Message ");
break;
case ACCEL_LOG_DEBUG:
fprintf(fLog, "Debug ");
break;
}
va_start(args, format);
vfprintf(fLog, format, args);
va_end(args);
fprintf(fLog, "\n");
switch (type) {
case ACCEL_LOG_ERROR:
zend_bailout();
break;
case ACCEL_LOG_FATAL:
exit(-2);
break;
}
fflush(fLog);
if (fLog != stderr) {
fclose(fLog);
}
}

33
zend_accelerator_debug.h

@ -0,0 +1,33 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_ACCELERATOR_DEBUG_H
#define ZEND_ACCELERATOR_DEBUG_H
#define ACCEL_LOG_FATAL 0
#define ACCEL_LOG_ERROR 1
#define ACCEL_LOG_WARNING 2
#define ACCEL_LOG_INFO 3
#define ACCEL_LOG_DEBUG 4
void zend_accel_error(int type, const char *format, ...);
#endif /* _ZEND_ACCELERATOR_DEBUG_H */

222
zend_accelerator_hash.c

@ -0,0 +1,222 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include "ZendAccelerator.h"
#include "zend_accelerator_hash.h"
#include "zend_hash.h"
#include "zend_shared_alloc.h"
/* Generated on an Octa-ALPHA 300MHz CPU & 2.5GB RAM monster */
static uint prime_numbers[] =
{5, 11, 19, 53, 107, 223, 463, 983, 1979, 3907, 7963, 16229, 32531, 65407, 130987, 262237, 524521, 1048793 };
static uint num_prime_numbers = sizeof(prime_numbers) / sizeof(uint);
void zend_accel_hash_clean(zend_accel_hash *accel_hash)
{
accel_hash->num_entries = 0;
accel_hash->num_direct_entries = 0;
memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
}
void zend_accel_hash_init(zend_accel_hash *accel_hash, zend_uint hash_size)
{
uint i;
for (i=0; i<num_prime_numbers; i++) {
if (hash_size <= prime_numbers[i]) {
hash_size = prime_numbers[i];
break;
}
}
accel_hash->num_entries = 0;
accel_hash->num_direct_entries = 0;
accel_hash->max_num_entries = hash_size;
/* set up hash pointers table */
accel_hash->hash_table = zend_shared_alloc(sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
if (!accel_hash->hash_table) {
zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
}
/* set up hash values table */
accel_hash->hash_entries = zend_shared_alloc(sizeof(zend_accel_hash_entry)*accel_hash->max_num_entries);
if (!accel_hash->hash_entries) {
zend_accel_error(ACCEL_LOG_FATAL, "Insufficient shared memory!");
}
memset(accel_hash->hash_table, 0, sizeof(zend_accel_hash_entry *)*accel_hash->max_num_entries);
}
/* Returns NULL if hash is full
* Returns pointer the actual hash entry on success
* key needs to be already allocated as it is not copied
*/
zend_accel_hash_entry* zend_accel_hash_update(zend_accel_hash *accel_hash, char *key, zend_uint key_length, zend_bool indirect, void *data)
{
zend_ulong hash_value;
zend_ulong index;
zend_accel_hash_entry *entry;
zend_accel_hash_entry *indirect_bucket = NULL;
if (indirect) {
indirect_bucket = (zend_accel_hash_entry*)data;
while (indirect_bucket->indirect) {
indirect_bucket = (zend_accel_hash_entry*)indirect_bucket->data;
}
}
hash_value = zend_inline_hash_func(key, key_length);
index = hash_value % accel_hash->max_num_entries;
/* try to see if the element already exists in the hash */
entry = accel_hash->hash_table[index];
while (entry) {
if (entry->hash_value == hash_value
&& entry->key_length == key_length
&& !memcmp(entry->key, key, key_length)) {
if (entry->indirect) {
if (indirect_bucket) {
entry->data = indirect_bucket;
} else {
((zend_accel_hash_entry*)entry->data)->data = data;
}
} else {
if (indirect_bucket) {
accel_hash->num_direct_entries--;
entry->data = indirect_bucket;
entry->indirect = 1;
} else {
entry->data = data;
}
}
return entry;
}
entry = entry->next;
}
/* Does not exist, add a new entry */
if (accel_hash->num_entries == accel_hash->max_num_entries) {
return NULL;
}
entry = &accel_hash->hash_entries[accel_hash->num_entries++];
if (indirect) {
entry->data = indirect_bucket;
entry->indirect = 1;
} else {
accel_hash->num_direct_entries++;
entry->data = data;
entry->indirect = 0;
}
entry->hash_value = hash_value;
entry->key = key;
entry->key_length = key_length;
entry->next = accel_hash->hash_table[index];
accel_hash->hash_table[index] = entry;
return entry;
}
/* Returns the data associated with key on success
* Returns NULL if data doesn't exist
*/
void* zend_accel_hash_find(zend_accel_hash *accel_hash, char *key, zend_uint key_length)
{
zend_ulong hash_value;
zend_ulong index;
zend_accel_hash_entry *entry;
hash_value = zend_inline_hash_func(key, key_length);
index = hash_value % accel_hash->max_num_entries;
entry = accel_hash->hash_table[index];
while (entry) {
if (entry->hash_value == hash_value
&& entry->key_length == key_length
&& !memcmp(entry->key, key, key_length)) {
if (entry->indirect) {
return ((zend_accel_hash_entry *) entry->data)->data;
} else {
return entry->data;
}
}
entry = entry->next;
}
return NULL;
}
/* Returns the hash entry associated with key on success
* Returns NULL if it doesn't exist
*/
zend_accel_hash_entry* zend_accel_hash_find_entry(zend_accel_hash *accel_hash, char *key, zend_uint key_length)
{
zend_ulong hash_value;
zend_ulong index;
zend_accel_hash_entry *entry;
hash_value = zend_inline_hash_func(key, key_length);
index = hash_value % accel_hash->max_num_entries;
entry = accel_hash->hash_table[index];
while (entry) {
if (entry->hash_value == hash_value
&& entry->key_length == key_length
&& !memcmp(entry->key, key, key_length)) {
if (entry->indirect) {
return (zend_accel_hash_entry *) entry->data;
} else {
return entry;
}
}
entry = entry->next;
}
return NULL;
}
int zend_accel_hash_unlink(zend_accel_hash *accel_hash, char *key, zend_uint key_length)
{
zend_ulong hash_value;
zend_ulong index;
zend_accel_hash_entry *entry, *last_entry=NULL;
hash_value = zend_inline_hash_func(key, key_length);
index = hash_value % accel_hash->max_num_entries;
entry = accel_hash->hash_table[index];
while (entry) {
if (entry->hash_value == hash_value
&& entry->key_length == key_length
&& !memcmp(entry->key, key, key_length)) {
if (!entry->indirect) {
accel_hash->num_direct_entries--;
}
if (last_entry) {
last_entry->next = entry->next;
} else {
accel_hash->hash_table[index] = entry->next;
}
return SUCCESS;
}
last_entry = entry;
entry = entry->next;
}
return FAILURE;
}

98
zend_accelerator_hash.h

@ -0,0 +1,98 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_ACCELERATOR_HASH_H
#define ZEND_ACCELERATOR_HASH_H
#include "zend.h"
/*
zend_accel_hash - is a hash table allocated in shared memory and
distributed across simultaneously running processes. The hash tables have
fixed sizen selected during construction by zend_accel_hash_init(). All the
hash entries are preallocated in the 'hash_entries' array. 'num_entries' is
initialized by zero and grows when new data is added.
zend_accel_hash_update() just takes the next entry from 'hash_entries'
array and puts it into appropriate place of 'hash_table'.
Hash collisions are resolved by separate chaining with linked lists,
however, entries are still taken from the same 'hash_entries' array.
'key' and 'data' passed to zend_accel_hash_update() must be already
allocated in shared memory. Few keys may be resolved to the same data.
using 'indirect' emtries, that point to other entries ('data' is actually
a pointer to another zend_accel_hash_entry).
zend_accel_hash_update() requires exclusive lock, however,
zend_accel_hash_find() does not.
*/
typedef struct _zend_accel_hash_entry zend_accel_hash_entry;
struct _zend_accel_hash_entry {
zend_ulong hash_value;
char *key;
zend_uint key_length;
zend_accel_hash_entry *next;
void *data;
zend_bool indirect;
};
typedef struct _zend_accel_hash {
zend_accel_hash_entry **hash_table;
zend_accel_hash_entry *hash_entries;
zend_uint num_entries;
zend_uint max_num_entries;
zend_uint num_direct_entries;
} zend_accel_hash;
void zend_accel_hash_init(zend_accel_hash *accel_hash, zend_uint hash_size);
void zend_accel_hash_clean(zend_accel_hash *accel_hash);
zend_accel_hash_entry* zend_accel_hash_update(
zend_accel_hash *accel_hash,
char *key,
zend_uint key_length,
zend_bool indirect,
void *data);
void* zend_accel_hash_find(
zend_accel_hash *accel_hash,
char *key,
zend_uint key_length);
zend_accel_hash_entry* zend_accel_hash_find_entry(
zend_accel_hash *accel_hash,
char *key,
zend_uint key_length);
int zend_accel_hash_unlink(
zend_accel_hash *accel_hash,
char *key,
zend_uint key_length);
static inline zend_bool zend_accel_hash_is_full(zend_accel_hash *accel_hash)
{
if (accel_hash->num_entries == accel_hash->max_num_entries) {
return 1;
} else {
return 0;
}
}
#endif /* ZEND_ACCELERATOR_HASH_H */

574
zend_accelerator_module.c

@ -0,0 +1,574 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include <time.h>
#include "php.h"
#include "ZendAccelerator.h"
#include "zend_API.h"
#include "zend_shared_alloc.h"
#include "zend_accelerator_blacklist.h"
#include "php_ini.h"
#include "SAPI.h"
#include "TSRM/tsrm_virtual_cwd.h"
#include "ext/standard/info.h"
#include "ext/standard/php_filestat.h"
#define STRING_NOT_NULL(s) (NULL == (s)?"":s)
#define MIN_ACCEL_FILES 200
#define MAX_ACCEL_FILES 100000
#define TOKENTOSTR(X) #X
/* User functions */
static ZEND_FUNCTION(accelerator_reset);
/* Private functions */
static ZEND_FUNCTION(accelerator_get_status);
static ZEND_FUNCTION(accelerator_get_configuration);
static zend_function_entry accel_functions[] = {
/* User functions */
ZEND_FE(accelerator_reset, NULL)
/* Private functions */
ZEND_FE(accelerator_get_configuration, NULL)
ZEND_FE(accelerator_get_status, NULL)
{ NULL, NULL, NULL, 0, 0 }
};
static ZEND_INI_MH(OnUpdateMemoryConsumption)
{
long *p;
long memsize;
#ifndef ZTS
char *base = (char *) mh_arg2;
#else
char *base = (char *) ts_resource(*((int *) mh_arg2));
#endif
/* keep the compiler happy */
(void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
p = (long *) (base+(size_t) mh_arg1);
memsize = atoi(new_value);
/* sanity check we must use at least 8 MB */
if (memsize < 8) {
const char *new_new_value = "8";
zend_ini_entry *ini_entry;
memsize = 8;
zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.memory_consumption is set below the required 8MB.\n" );
zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the minimal 8MB cofiguration.\n" );
if (zend_hash_find(EG(ini_directives),
"zend_optimizerplus.memory_consumption",
sizeof("zend_optimizerplus.memory_consumption"),
(void *) &ini_entry)==FAILURE) {
return FAILURE;
}
ini_entry->value = strdup(new_new_value);
ini_entry->value_length = strlen(new_new_value);
}
*p = memsize * (1024 * 1024);
return SUCCESS;
}
static ZEND_INI_MH(OnUpdateMaxAcceleratedFiles)
{
long *p;
long size;
#ifndef ZTS
char *base = (char *) mh_arg2;
#else
char *base = (char *) ts_resource(*((int *) mh_arg2));
#endif
/* keep the compiler happy */
(void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
p = (long *) (base+(size_t) mh_arg1);
size = atoi(new_value);
/* sanity check we must use a value between MIN_ACCEL_FILES and MAX_ACCEL_FILES */
if (size < MIN_ACCEL_FILES || size > MAX_ACCEL_FILES) {
const char *new_new_value;
zend_ini_entry *ini_entry;
if(size < MIN_ACCEL_FILES){
size = MIN_ACCEL_FILES;
new_new_value = TOKENTOSTR(MIN_ACCEL_FILES);
zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_accelerated_files is set below the required minimum (%d).\n", MIN_ACCEL_FILES );
zend_accel_error(ACCEL_LOG_WARNING,ACCELERATOR_PRODUCT_NAME " will use the minimal cofiguration.\n" );
}
if(size > MAX_ACCEL_FILES){
size = MAX_ACCEL_FILES;
new_new_value = TOKENTOSTR(MAX_ACCEL_FILES);
zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_accelerated_files is set above the limit (%d).\n", MAX_ACCEL_FILES );
zend_accel_error(ACCEL_LOG_WARNING, ACCELERATOR_PRODUCT_NAME " will use the maximal cofiguration.\n" );
}
if (zend_hash_find(EG(ini_directives),
"zend_optimizerplus.max_accelerated_files",
sizeof("zend_optimizerplus.max_accelerated_files"),
(void *) &ini_entry)==FAILURE) {
return FAILURE;
}
ini_entry->value = strdup(new_new_value);
ini_entry->value_length = strlen(new_new_value);
}
*p = size;
return SUCCESS;
}
static ZEND_INI_MH(OnUpdateMaxWastedPercentage)
{
double *p;
long percentage;
#ifndef ZTS
char *base = (char *) mh_arg2;
#else
char *base = (char *) ts_resource(*((int *) mh_arg2));
#endif
/* keep the compiler happy */
(void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
p = (double *) (base+(size_t) mh_arg1);
percentage = atoi(new_value);
if (percentage <= 0 || percentage > 50) {
const char *new_new_value = "5";
zend_ini_entry *ini_entry;
percentage = 5;
zend_accel_error(ACCEL_LOG_WARNING,"zend_optimizerplus.max_wasted_percentage must be ser netweeb 1 and 50.\n");
zend_accel_error(ACCEL_LOG_WARNING,ACCELERATOR_PRODUCT_NAME " will use 5%.\n" );
if (zend_hash_find(EG(ini_directives),
"zend_optimizerplus.max_wasted_percentage",
sizeof("zend_optimizerplus.max_wasted_percentage"),
(void *) &ini_entry)==FAILURE) {
return FAILURE;
}
ini_entry->value = strdup(new_new_value);
ini_entry->value_length = strlen(new_new_value);
}
*p = (double)percentage / 100.0;
return SUCCESS;
}
static ZEND_INI_MH(OnUpdateAccelBlacklist)
{
char **p;
#ifndef ZTS
char *base = (char *) mh_arg2;
#else
char *base = (char *) ts_resource(*((int *) mh_arg2));
#endif
/* keep the compiler happy */
(void)entry; (void)new_value_length; (void)mh_arg2; (void)mh_arg3; (void)stage;
if (new_value && !new_value[0]) {
return FAILURE;
}
p = (char **) (base+(size_t) mh_arg1);
*p = new_value;
zend_accel_blacklist_init(&accel_blacklist);
zend_accel_blacklist_load(&accel_blacklist, *p);
return SUCCESS;
}
ZEND_INI_BEGIN()
STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable" ,"1", PHP_INI_SYSTEM, OnUpdateBool, enabled , zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("zend_optimizerplus.use_cwd" ,"1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.use_cwd , zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("zend_optimizerplus.validate_timestamps","1", PHP_INI_ALL , OnUpdateBool, accel_directives.validate_timestamps, zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("zend_optimizerplus.inherited_hack" ,"1", PHP_INI_SYSTEM, OnUpdateBool, accel_directives.inherited_hack , zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("zend_optimizerplus.dups_fix" ,"0", PHP_INI_ALL , OnUpdateBool, accel_directives.ignore_dups , zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("zend_optimizerplus.revalidate_path" ,"0", PHP_INI_ALL , OnUpdateBool, accel_directives.revalidate_path , zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.log_verbosity_level" , "1" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.log_verbosity_level, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.memory_consumption" , "64" , PHP_INI_SYSTEM, OnUpdateMemoryConsumption, accel_directives.memory_consumption, zend_accel_globals, accel_globals)
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
STD_PHP_INI_ENTRY("zend_optimizerplus.interned_strings_buffer", "4" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.interned_strings_buffer, zend_accel_globals, accel_globals)
#endif
STD_PHP_INI_ENTRY("zend_optimizerplus.max_accelerated_files" , "2000", PHP_INI_SYSTEM, OnUpdateMaxAcceleratedFiles, accel_directives.max_accelerated_files, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.max_wasted_percentage" , "5" , PHP_INI_SYSTEM, OnUpdateMaxWastedPercentage, accel_directives.max_wasted_percentage, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.consistency_checks" , "0" , PHP_INI_ALL , OnUpdateLong, accel_directives.consistency_checks, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.force_restart_timeout" , "180" , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.force_restart_timeout, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.revalidate_freq" , "2" , PHP_INI_ALL , OnUpdateLong, accel_directives.revalidate_freq, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.preferred_memory_model", "" , PHP_INI_SYSTEM, OnUpdateStringUnempty, accel_directives.memory_model, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.blacklist_filename" , "" , PHP_INI_SYSTEM, OnUpdateAccelBlacklist, accel_directives.user_blacklist_filename, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.protect_memory" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.protect_memory, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.save_comments" , "1" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.save_comments, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.fast_shutdown" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.fast_shutdown, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.optimization_level" , DEFAULT_OPTIMIZATION_LEVEL , PHP_INI_SYSTEM, OnUpdateLong, accel_directives.optimization_level, zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable_file_override" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.file_override_enabled, zend_accel_globals, accel_globals)
STD_PHP_INI_BOOLEAN("zend_optimizerplus.enable_cli" , "0" , PHP_INI_SYSTEM, OnUpdateBool, accel_directives.enable_cli, zend_accel_globals, accel_globals)
STD_PHP_INI_ENTRY("zend_optimizerplus.error_log" , "" , PHP_INI_SYSTEM, OnUpdateString, accel_directives.error_log, zend_accel_globals, accel_globals)
#ifdef ZEND_WIN32
STD_PHP_INI_ENTRY("zend_optimizerplus.mmap_base", NULL, PHP_INI_SYSTEM, OnUpdateString, accel_directives.mmap_base, zend_accel_globals, accel_globals)
#endif
ZEND_INI_END()
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
#undef EX
#define EX(element) execute_data->element
#define EX_T(offset) (*(temp_variable *)((char *) EX(Ts) + offset))
static int ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER(ZEND_OPCODE_HANDLER_ARGS)
{
zend_class_entry **pce, **pce_orig;
if (zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op2.u.constant), Z_STRLEN(EX(opline)->op2.u.constant)+1, (void **)&pce) == FAILURE ||
(zend_hash_find(EG(class_table), Z_STRVAL(EX(opline)->op1.u.constant), Z_STRLEN(EX(opline)->op1.u.constant), (void**)&pce_orig) == SUCCESS &&
*pce != *pce_orig)) {
do_bind_inherited_class(EX(opline), EG(class_table), EX_T(EX(opline)->extended_value).class_entry, 0 TSRMLS_CC);
}
EX(opline)++;
return ZEND_USER_OPCODE_CONTINUE;
}
#endif
static int filename_is_in_cache(char *filename, int filename_len TSRMLS_DC)
{
char *key;
int key_length;
zend_file_handle handle = {0};
zend_persistent_script *persistent_script;
handle.filename = filename;
handle.type = ZEND_HANDLE_FILENAME;
if (IS_ABSOLUTE_PATH(filename, filename_len)) {
persistent_script = zend_accel_hash_find(&ZCSG(hash), filename, filename_len+1);
if (persistent_script) {
return !persistent_script->corrupted;
}
}
if((key = accel_make_persistent_key_ex(&handle, filename_len, &key_length TSRMLS_CC)) != NULL) {
persistent_script = zend_accel_hash_find(&ZCSG(hash), key, key_length+1);
return persistent_script && !persistent_script->corrupted;
}
return 0;
}
static void accel_file_in_cache(int type, INTERNAL_FUNCTION_PARAMETERS)
{
char *filename;
int filename_len;
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
zval **zfilename;
if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &zfilename) == FAILURE) {
WRONG_PARAM_COUNT;
}
convert_to_string_ex(zfilename);
filename = Z_STRVAL_PP(zfilename);
filename_len = Z_STRLEN_PP(zfilename);
#else
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "p", &filename, &filename_len) == FAILURE) {
return;
}
#endif
if(filename_len > 0) {
if(filename_is_in_cache(filename, filename_len TSRMLS_CC)) {
RETURN_TRUE;
}
}
php_stat(filename, filename_len, type, return_value TSRMLS_CC);
}
static void accel_file_exists(INTERNAL_FUNCTION_PARAMETERS)
{
accel_file_in_cache(FS_EXISTS, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
static void accel_is_file(INTERNAL_FUNCTION_PARAMETERS)
{
accel_file_in_cache(FS_IS_FILE, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
static void accel_is_readable(INTERNAL_FUNCTION_PARAMETERS)
{
accel_file_in_cache(FS_IS_R, INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
static ZEND_MINIT_FUNCTION(zend_accelerator)
{
(void)type; /* keep the compiler happy */
/* must be 0 before the ini entry OnUpdate function is called */
accel_blacklist.entries = NULL;
REGISTER_INI_ENTRIES();
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
zend_set_user_opcode_handler(ZEND_DECLARE_INHERITED_CLASS_DELAYED, ZEND_DECLARE_INHERITED_CLASS_DELAYED_HANDLER);
#endif
return SUCCESS;
}
void zend_accel_override_file_functions(TSRMLS_D)
{
zend_function *old_function;
if(ZCG(startup_ok) && ZCG(accel_directives).file_override_enabled) {
/* override file_exists */
if(zend_hash_find(CG(function_table), "file_exists", sizeof("file_exists"), (void **)&old_function) == SUCCESS) {
old_function->internal_function.handler = accel_file_exists;
}
if(zend_hash_find(CG(function_table), "is_file", sizeof("is_file"), (void **)&old_function) == SUCCESS) {
old_function->internal_function.handler = accel_is_file;
}
if(zend_hash_find(CG(function_table), "is_readable", sizeof("is_readable"), (void **)&old_function) == SUCCESS) {
old_function->internal_function.handler = accel_is_readable;
}
}
}
static ZEND_MSHUTDOWN_FUNCTION(zend_accelerator)
{
(void)type; /* keep the compiler happy */
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
void zend_accel_info(ZEND_MODULE_INFO_FUNC_ARGS)
{
php_info_print_table_start();
if (ZCG(startup_ok) && ZCSG(accelerator_enabled)) {
php_info_print_table_row(2, "Opcode Caching", "Up and Running");
} else {
php_info_print_table_row(2, "Opcode Caching", "Disabled");
}
if (ZCG(enabled) && ZCG(accel_directives).optimization_level) {
php_info_print_table_row(2, "Optimization", "Enabled");
} else {
php_info_print_table_row(2, "Optimization", "Disabled");
}
if (!ZCG(startup_ok) || zps_api_failure_reason) {
php_info_print_table_row(2, "Startup Failed", zps_api_failure_reason);
} else {
php_info_print_table_row(2, "Startup", "OK");
php_info_print_table_row(2, "Shared memory model", zend_accel_get_shared_model());
}
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
static zend_module_entry accel_module_entry = {
STANDARD_MODULE_HEADER,
ACCELERATOR_PRODUCT_NAME,
accel_functions,
ZEND_MINIT(zend_accelerator),
ZEND_MSHUTDOWN(zend_accelerator),
NULL,
NULL,
zend_accel_info,
ACCELERATOR_VERSION "FE",
STANDARD_MODULE_PROPERTIES
};
int start_accel_module()
{
return zend_startup_module(&accel_module_entry);
}
/* {{{ proto array accelerator_get_scripts()
Get the scripts which are accelerated by ZendAccelerator */
static zval* accelerator_get_scripts(TSRMLS_D)
{
uint i;
zval *return_value,*persistent_script_report;
zend_accel_hash_entry *cache_entry;
struct tm *ta;
struct timeval exec_time;
struct timeval fetch_time;
if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled) || accelerator_shm_read_lock(TSRMLS_C) != SUCCESS) {
return 0;
}
MAKE_STD_ZVAL(return_value);
array_init(return_value);
for (i=0; i<ZCSG(hash).max_num_entries; i++) {
for (cache_entry=ZCSG(hash).hash_table[i]; cache_entry; cache_entry = cache_entry->next) {
zend_persistent_script *script;
if (cache_entry->indirect) continue;
script = (zend_persistent_script *)cache_entry->data;
MAKE_STD_ZVAL(persistent_script_report);
array_init(persistent_script_report);
add_assoc_stringl(persistent_script_report, "full_path", script->full_path, script->full_path_len, 1);
add_assoc_long(persistent_script_report, "hits", script->dynamic_members.hits);
add_assoc_long(persistent_script_report, "memory_consumption", script->dynamic_members.memory_consumption);
ta = localtime(&script->dynamic_members.last_used);
add_assoc_string(persistent_script_report, "last_used", asctime(ta), 1);
add_assoc_long(persistent_script_report, "last_used_timestamp", script->dynamic_members.last_used);
if (ZCG(accel_directives).validate_timestamps) {
add_assoc_long(persistent_script_report, "timestamp", (long)script->timestamp);
}
timerclear(&exec_time);
timerclear(&fetch_time);
zend_hash_update(return_value->value.ht, cache_entry->key, cache_entry->key_length, &persistent_script_report, sizeof(zval *), NULL);
}
}
accelerator_shm_read_unlock(TSRMLS_C);
return return_value;
}
/* {{{ proto array accelerator_get_status()
Obtain statistics information regarding code acceleration in the Zend Performance Suite */
static ZEND_FUNCTION(accelerator_get_status)
{
long reqs;
zval *memory_usage,*statistics,*scripts;
/* keep the compiler happy */
(void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled)) {
RETURN_FALSE;
}
array_init(return_value);
/* Trivia */
add_assoc_long(return_value, "accelerator_enabled", ZCG(startup_ok) && ZCSG(accelerator_enabled));
add_assoc_bool(return_value, "cache_full", ZSMMG(memory_exhausted));
/* Memory usage statistics */
MAKE_STD_ZVAL(memory_usage);
array_init(memory_usage);
add_assoc_long(memory_usage, "used_memory", ZCG(accel_directives).memory_consumption-zend_shared_alloc_get_free_memory()-ZSMMG(wasted_shared_memory));
add_assoc_long(memory_usage, "free_memory", zend_shared_alloc_get_free_memory());
add_assoc_long(memory_usage, "wasted_memory", ZSMMG(wasted_shared_memory));
add_assoc_double(memory_usage, "current_wasted_percentage", (((double) ZSMMG(wasted_shared_memory))/ZCG(accel_directives).memory_consumption)*100.0);
add_assoc_zval(return_value, "memory_usage",memory_usage);
/* Accelerator statistics */
MAKE_STD_ZVAL(statistics);
array_init(statistics);
add_assoc_long(statistics, "num_cached_scripts", ZCSG(hash).num_direct_entries);
add_assoc_long(statistics, "max_cached_scripts", ZCSG(hash).max_num_entries);
add_assoc_long(statistics, "hits", ZCSG(hits));
add_assoc_long(statistics, "last_restart_time", ZCSG(last_restart_time));
add_assoc_long(statistics, "misses", ZSMMG(memory_exhausted)?ZCSG(misses):ZCSG(misses)-ZCSG(blacklist_misses));
add_assoc_long(statistics, "blacklist_misses", ZCSG(blacklist_misses));
reqs = ZCSG(hits)+ZCSG(misses);
add_assoc_double(statistics, "blacklist_miss_ratio", reqs?(((double) ZCSG(blacklist_misses))/reqs)*100.0:0);
add_assoc_double(statistics, "accelerator_hit_rate", reqs?(((double) ZCSG(hits))/reqs)*100.0:0);
add_assoc_zval(return_value, "accelerator_statistics",statistics);
/* acceleratred scripts */
scripts=accelerator_get_scripts(TSRMLS_C);
if( scripts ){
add_assoc_zval(return_value, "scripts",scripts);
}
}
static int add_blacklist_path(zend_blacklist_entry *p, zval *return_value TSRMLS_DC)
{
add_next_index_stringl(return_value, p->path, p->path_length, 1);
return 0;
}
/* {{{ proto array accelerator_get_configuration()
Obtain configuration information for the Zend Performance Suite */
static ZEND_FUNCTION(accelerator_get_configuration)
{
zval *directives,*version,*blacklist;
/* keep the compiler happy */
(void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
array_init(return_value);
/* directives */
MAKE_STD_ZVAL(directives);
array_init(directives);
add_assoc_bool(directives, "zend_optimizerplus.enable", ZCG(enabled));
add_assoc_bool(directives, "zend_optimizerplus.use_cwd", ZCG(accel_directives).use_cwd);
add_assoc_bool(directives, "zend_optimizerplus.validate_timestamps", ZCG(accel_directives).validate_timestamps);
add_assoc_bool(directives, "zend_optimizerplus.inherited_hack", ZCG(accel_directives).inherited_hack);
add_assoc_bool(directives, "zend_optimizerplus.dups_fix", ZCG(accel_directives).ignore_dups);
add_assoc_bool(directives, "zend_optimizerplus.revalidate_path", ZCG(accel_directives).revalidate_path);
add_assoc_long(directives, "zend_optimizerplus.log_verbosity_level", ZCG(accel_directives).log_verbosity_level);
add_assoc_long(directives, "zend_optimizerplus.memory_consumption", ZCG(accel_directives).memory_consumption);
add_assoc_long(directives, "zend_optimizerplus.max_accelerated_files", ZCG(accel_directives).max_accelerated_files);
add_assoc_double(directives, "zend_optimizerplus.max_wasted_percentage", ZCG(accel_directives).max_wasted_percentage);
add_assoc_long(directives, "zend_optimizerplus.consistency_checks", ZCG(accel_directives).consistency_checks);
add_assoc_long(directives, "zend_optimizerplus.force_restart_timeout", ZCG(accel_directives).force_restart_timeout);
add_assoc_long(directives, "zend_optimizerplus.revalidate_freq", ZCG(accel_directives).revalidate_freq);
add_assoc_string(directives, "zend_optimizerplus.preferred_memory_model", STRING_NOT_NULL(ZCG(accel_directives).memory_model), 1);
add_assoc_string(directives, "zend_optimizerplus.blacklist_filename", STRING_NOT_NULL(ZCG(accel_directives).user_blacklist_filename), 1);
add_assoc_bool(directives, "zend_optimizerplus.protect_memory", ZCG(accel_directives).protect_memory);
add_assoc_bool(directives, "zend_optimizerplus.save_comments", ZCG(accel_directives).save_comments);
add_assoc_bool(directives, "zend_optimizerplus.fast_shutdown", ZCG(accel_directives).fast_shutdown);
add_assoc_long(directives, "zend_optimizerplus.optimization_level", ZCG(accel_directives).optimization_level);
add_assoc_zval(return_value,"directives",directives);
/*version */
MAKE_STD_ZVAL(version);
array_init(version);
add_assoc_string(version, "version", ACCELERATOR_VERSION, 1);
add_assoc_string(version, "accelerator_product_name", ACCELERATOR_PRODUCT_NAME, 1);
add_assoc_zval(return_value,"version",version);
/* blacklist */
MAKE_STD_ZVAL(blacklist);
array_init(blacklist);
zend_accel_blacklist_apply(&accel_blacklist, (apply_func_arg_t) add_blacklist_path, blacklist TSRMLS_CC);
add_assoc_zval(return_value,"blacklist",blacklist);
}
/* {{{ proto void accelerator_reset()
Request that the contents of the Accelerator module in the ZPS be reset */
static ZEND_FUNCTION(accelerator_reset)
{
/* keep the compiler happy */
(void)ht; (void)return_value_ptr; (void)this_ptr; (void)return_value_used;
if (!ZCG(startup_ok) || !ZCSG(accelerator_enabled)) {
RETURN_FALSE;
}
zend_accel_schedule_restart(TSRMLS_C);
RETURN_TRUE;
}

28
zend_accelerator_module.h

@ -0,0 +1,28 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_ACCELERAROR_MODULE_H
#define ZEND_ACCELERATOR_MODULE_H
int start_accel_module();
void zend_accel_override_file_functions(TSRMLS_D);
#endif /* _ZEND_ACCELERATOR_MODULE_H */

1079
zend_accelerator_util_funcs.c
File diff suppressed because it is too large
View File

49
zend_accelerator_util_funcs.h

@ -0,0 +1,49 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_ACCELERATOR_UTIL_FUNCS_H
#define ZEND_ACCELERATOR_UTIL_FUNCS_H
#include "zend.h"
#include "ZendAccelerator.h"
void zend_accel_copy_internal_functions(TSRMLS_D);
zend_persistent_script* create_persistent_script(void);
void free_persistent_script(zend_persistent_script *persistent_script, int destroy_elements);
void zend_accel_free_user_functions(HashTable *ht TSRMLS_DC);
void zend_accel_move_user_functions(HashTable *str, HashTable *dst TSRMLS_DC);
zend_op_array* zend_accel_load_script(zend_persistent_script *persistent_script, int from_shared_memory TSRMLS_DC);
#define ADLER32_INIT 1 /* initial Adler-32 value */
unsigned int zend_adler32(unsigned int checksum, signed char *buf, uint len);
#endif /* ZEND_ACCELERATOR_UTIL_FUNCS_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/

680
zend_persist.c

@ -0,0 +1,680 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include "zend.h"
#include "ZendAccelerator.h"
#include "zend_persist.h"
#include "zend_extensions.h"
#include "zend_shared_alloc.h"
#include "zend_vm.h"
#include "zend_constants.h"
#include "zend_operators.h"
#define zend_accel_store(p, size) \
(p = _zend_shared_memdup((void*)p, size, 1 TSRMLS_CC))
#define zend_accel_memdup(p, size) \
_zend_shared_memdup((void*)p, size, 0 TSRMLS_CC)
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
# define zend_accel_memdup_interned_string(str, len) \
IS_INTERNED(str) ? str : zend_accel_memdup(str, len)
# define zend_accel_store_interned_string(str, len) do { \
if (!IS_INTERNED(str)) { zend_accel_store(str, len); } \
} while (0)
#else
# define zend_accel_memdup_interned_string(str, len) \
zend_accel_memdup(str, len)
# define zend_accel_store_interned_string(str, len) \
zend_accel_store(str, len)
#endif
typedef void (*zend_persist_func_t)(void * TSRMLS_DC);
static void zend_persist_zval_ptr(zval **zp TSRMLS_DC);
static void zend_hash_persist(HashTable *ht, void (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC)
{
Bucket *p = ht->pListHead;
uint i;
while (p) {
Bucket *q = p;
/* persist bucket and key */
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
p = zend_accel_memdup(p, sizeof(Bucket));
if (p->nKeyLength) {
p->arKey = zend_accel_memdup_interned_string(p->arKey, p->nKeyLength);
}
#else
p = zend_accel_memdup(p, sizeof(Bucket) - 1 + p->nKeyLength);
#endif
/* persist data pointer in bucket */
if (!p->pDataPtr) {
zend_accel_store(p->pData, el_size);
} else {
/* Update p->pData to point to the new p->pDataPtr address, after the bucket relocation */
p->pData = &p->pDataPtr;
}
/* persist the data itself */
if (pPersistElement) {
pPersistElement(p->pData TSRMLS_CC);
}
/* update linked lists */
if (p->pLast) {
p->pLast->pNext = p;
}
if (p->pNext) {
p->pNext->pLast = p;
}
if (p->pListLast) {
p->pListLast->pListNext = p;
}
if (p->pListNext) {
p->pListNext->pListLast = p;
}
p = p->pListNext;
/* delete the old non-persistent bucket */
efree(q);
}
/* update linked lists */
if (ht->pListHead) {
ht->pListHead = zend_shared_alloc_get_xlat_entry(ht->pListHead);
}
if (ht->pListTail) {
ht->pListTail = zend_shared_alloc_get_xlat_entry(ht->pListTail);
}
if (ht->pInternalPointer) {
ht->pInternalPointer = zend_shared_alloc_get_xlat_entry(ht->pInternalPointer);
}
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
/* Check if HastTable is initialized */
if (ht->nTableMask) {
#endif
if (ht->nNumOfElements) {
/* update hash table */
for (i = 0; i < ht->nTableSize; i++) {
if (ht->arBuckets[i]) {
ht->arBuckets[i] = zend_shared_alloc_get_xlat_entry(ht->arBuckets[i]);
}
}
}
zend_accel_store(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize);
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
} else {
ht->arBuckets = NULL;
}
#endif
}
static void zend_persist_zval(zval *z TSRMLS_DC)
{
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
switch (z->type & IS_CONSTANT_TYPE_MASK) {
#else
switch (z->type & ~IS_CONSTANT_INDEX) {
#endif
case IS_STRING:
case IS_CONSTANT:
zend_accel_store_interned_string(z->value.str.val, z->value.str.len + 1);
break;
case IS_ARRAY:
case IS_CONSTANT_ARRAY:
zend_accel_store(z->value.ht, sizeof(HashTable));
zend_hash_persist(z->value.ht, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
break;
}
}
static void zend_persist_zval_ptr(zval **zp TSRMLS_DC)
{
zval *new_ptr = zend_shared_alloc_get_xlat_entry(*zp);
if (new_ptr) {
*zp = new_ptr;
} else {
/* Attempt to store only if we didn't store this zval_ptr yet */
zend_accel_store(*zp, sizeof(zval));
zend_persist_zval(*zp TSRMLS_CC);
}
}
static void zend_protect_zval(zval *z TSRMLS_DC)
{
PZ_SET_ISREF_P(z);
PZ_SET_REFCOUNT_P(z,2);
}
static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_script* main_persistent_script TSRMLS_DC)
{
zend_op *persist_ptr;
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
int has_jmp = 0;
#endif
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
zend_literal *orig_literals = NULL;
#endif
if (op_array->type != ZEND_USER_FUNCTION) {
return;
}
#if ZEND_EXTENSION_API_NO <= PHP_5_3_X_API_NO
op_array->size = op_array->last;
#endif
if (--(*op_array->refcount) == 0) {
efree(op_array->refcount);
}
op_array->refcount = NULL;
if (op_array->filename) {
/* do not free! PHP has centralized filename storage, compiler will free it */
op_array->filename = zend_accel_memdup(op_array->filename, strlen(op_array->filename) + 1);
}
if (main_persistent_script) {
zend_bool orig_in_execution = EG(in_execution);
zend_op_array *orig_op_array = EG(active_op_array);
zval offset;
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
main_persistent_script->early_binding = -1;
#endif
EG(in_execution) = 1;
EG(active_op_array) = op_array;
if (zend_get_constant("__COMPILER_HALT_OFFSET__", sizeof("__COMPILER_HALT_OFFSET__")-1, &offset TSRMLS_CC)) {
main_persistent_script->compiler_halt_offset = Z_LVAL(offset);
}
EG(active_op_array) = orig_op_array;
EG(in_execution) = orig_in_execution;
}
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
if (op_array->literals) {
orig_literals = zend_shared_alloc_get_xlat_entry(op_array->literals);
if (orig_literals) {
op_array->literals = orig_literals;
} else {
zend_literal *p = zend_accel_memdup(op_array->literals, sizeof(zend_literal) * op_array->last_literal);
zend_literal *end = p + op_array->last_literal;
orig_literals = op_array->literals;
op_array->literals = p;
while (p < end) {
zend_persist_zval(&p->constant TSRMLS_CC);
zend_protect_zval(&p->constant TSRMLS_CC);
p++;
}
efree(orig_literals);
}
}
#endif
if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->opcodes))) {
op_array->opcodes = persist_ptr;
} else {
zend_op *new_opcodes = zend_accel_memdup(op_array->opcodes, sizeof(zend_op) * op_array->last);
zend_op *opline = new_opcodes;
zend_op *end = new_opcodes + op_array->last;
int offset = 0;
for (;opline<end;opline++, offset++) {
if (ZEND_OP1_TYPE(opline)==IS_CONST) {
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
opline->op1.zv = (zval*)((char*)opline->op1.zv + ((char*)op_array->literals - (char*)orig_literals));
#else
zend_persist_zval(&opline->op1.u.constant TSRMLS_CC);
zend_protect_zval(&opline->op1.u.constant TSRMLS_CC);
#endif
}
if (ZEND_OP2_TYPE(opline)==IS_CONST) {
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
opline->op2.zv = (zval*)((char*)opline->op2.zv + ((char*)op_array->literals - (char*)orig_literals));
#else
zend_persist_zval(&opline->op2.u.constant TSRMLS_CC);
zend_protect_zval(&opline->op2.u.constant TSRMLS_CC);
#endif
}
#if ZEND_EXTENSION_API_NO < PHP_5_3_X_API_NO
switch (opline->opcode) {
case ZEND_JMP:
has_jmp = 1;
if (ZEND_DONE_PASS_TWO(op_array)) {
ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes];
}
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
has_jmp = 1;
if (ZEND_DONE_PASS_TWO(op_array)) {
ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes];
}
break;
case ZEND_JMPZNZ:
case ZEND_BRK:
case ZEND_CONT:
has_jmp = 1;
break;
case ZEND_DECLARE_INHERITED_CLASS:
if (main_persistent_script && ZCG(accel_directives).inherited_hack) {
if (!has_jmp &&
((opline+2) >= end ||
(opline+1)->opcode != ZEND_FETCH_CLASS ||
(opline+2)->opcode != ZEND_ADD_INTERFACE)) {
zend_uint *opline_num = &main_persistent_script->early_binding;
while ((int)*opline_num != -1) {
opline_num = &new_opcodes[*opline_num].result.u.opline_num;
}
*opline_num = opline - new_opcodes;
opline->result.op_type = IS_UNUSED;
opline->result.u.opline_num = -1;
opline->opcode = ZEND_DECLARE_INHERITED_CLASS_DELAYED;
ZEND_VM_SET_OPCODE_HANDLER(opline);
}
break;
}
}
#else /* if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO */
if (ZEND_DONE_PASS_TWO(op_array)) {
/* fix jmps to point to new array */
switch (opline->opcode) {
case ZEND_JMP:
case ZEND_GOTO:
#if ZEND_EXTENSION_API_NO > PHP_5_4_X_API_NO
case ZEND_FAST_CALL:
#endif
ZEND_OP1(opline).jmp_addr = &new_opcodes[ZEND_OP1(opline).jmp_addr - op_array->opcodes];
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
case ZEND_JMP_SET_VAR:
#endif
ZEND_OP2(opline).jmp_addr = &new_opcodes[ZEND_OP2(opline).jmp_addr - op_array->opcodes];
break;
}
}
#endif /* if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO */
}
efree(op_array->opcodes);
op_array->opcodes = new_opcodes;
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
if (op_array->run_time_cache) {
efree(op_array->run_time_cache);
op_array->run_time_cache = NULL;
}
#endif
}
if (op_array->function_name) {
char *new_name;
if ((new_name = zend_shared_alloc_get_xlat_entry(op_array->function_name))) {
op_array->function_name = new_name;
} else {
zend_accel_store(op_array->function_name, strlen(op_array->function_name)+1);
}
}
if (op_array->arg_info) {
zend_arg_info *new_ptr;
if((new_ptr = zend_shared_alloc_get_xlat_entry(op_array->arg_info))) {
op_array->arg_info = new_ptr;
} else {
zend_uint i;
zend_accel_store(op_array->arg_info, sizeof(zend_arg_info) * op_array->num_args);
for(i=0;i<op_array->num_args;i++) {
if(op_array->arg_info[i].name) {
zend_accel_store_interned_string(op_array->arg_info[i].name, op_array->arg_info[i].name_len + 1);
}
if(op_array->arg_info[i].class_name) {
zend_accel_store_interned_string(op_array->arg_info[i].class_name, op_array->arg_info[i].class_name_len + 1);
}
}
}
}
if (op_array->brk_cont_array) {
zend_accel_store(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
}
if (op_array->static_variables) {
zend_hash_persist(op_array->static_variables, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
zend_accel_store(op_array->static_variables, sizeof(HashTable));
}
if(op_array->scope) {
op_array->scope = zend_shared_alloc_get_xlat_entry(op_array->scope);
}
if(op_array->doc_comment) {
if (ZCG(accel_directives).save_comments) {
zend_accel_store(op_array->doc_comment, op_array->doc_comment_len + 1);
} else {
if(!zend_shared_alloc_get_xlat_entry(op_array->doc_comment)) {
zend_shared_alloc_register_xlat_entry(op_array->doc_comment, op_array->doc_comment);
efree((char*)op_array->doc_comment);
}
op_array->doc_comment = NULL;
op_array->doc_comment_len = 0;
}
}
if(op_array->try_catch_array) {
zend_accel_store(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
}
if(op_array->vars) {
if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->vars))) {
op_array->vars = (zend_compiled_variable*)persist_ptr;
} else {
int i;
zend_accel_store(op_array->vars, sizeof(zend_compiled_variable) * op_array->last_var);
for(i=0; i<op_array->last_var; i++) {
zend_accel_store_interned_string(op_array->vars[i].name, op_array->vars[i].name_len + 1);
}
}
}
/* "prototype" may be undefined if "scope" isn't set */
if(op_array->scope && op_array->prototype) {
if ((persist_ptr = zend_shared_alloc_get_xlat_entry(op_array->prototype))) {
op_array->prototype = (union _zend_function*)persist_ptr;
/* we use refcount to show that op_array is referenced from several places */
op_array->prototype->op_array.refcount++;
}
} else {
op_array->prototype = NULL;
}
}
static void zend_persist_op_array(zend_op_array *op_array TSRMLS_DC)
{
zend_persist_op_array_ex(op_array, NULL TSRMLS_CC);
}
static void zend_persist_property_info(zend_property_info *prop TSRMLS_DC)
{
zend_accel_store_interned_string(prop->name, prop->name_length + 1);
if(prop->doc_comment) {
if (ZCG(accel_directives).save_comments) {
zend_accel_store(prop->doc_comment, prop->doc_comment_len + 1);
} else {
if(!zend_shared_alloc_get_xlat_entry(prop->doc_comment)) {
zend_shared_alloc_register_xlat_entry(prop->doc_comment, prop->doc_comment);
efree((char*)prop->doc_comment);
}
prop->doc_comment = NULL;
prop->doc_comment_len = 0;
}
}
}
static void zend_persist_class_entry(zend_class_entry **pce TSRMLS_DC)
{
zend_class_entry *ce = *pce;
if (ce->type == ZEND_USER_CLASS) {
*pce = zend_accel_store(ce, sizeof(zend_class_entry));
zend_accel_store_interned_string(ce->name, ce->name_length + 1);
zend_hash_persist(&ce->function_table, (zend_persist_func_t) zend_persist_op_array, sizeof(zend_op_array) TSRMLS_CC);
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
if (ce->default_properties_table) {
int i;
zend_accel_store(ce->default_properties_table, sizeof(zval*) * ce->default_properties_count);
for (i = 0; i < ce->default_properties_count; i++) {
if (ce->default_properties_table[i]) {
zend_persist_zval_ptr(&ce->default_properties_table[i] TSRMLS_CC);
}
}
}
if (ce->default_static_members_table) {
int i;
zend_accel_store(ce->default_static_members_table, sizeof(zval*) * ce->default_static_members_count);
for (i = 0; i < ce->default_static_members_count; i++) {
if (ce->default_static_members_table[i]) {
zend_persist_zval_ptr(&ce->default_static_members_table[i] TSRMLS_CC);
}
}
}
ce->static_members_table = NULL;
#else
zend_hash_persist(&ce->default_properties, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
zend_hash_persist(&ce->default_static_members, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
ce->static_members = NULL;
#endif
zend_hash_persist(&ce->constants_table, (zend_persist_func_t) zend_persist_zval_ptr, sizeof(zval**) TSRMLS_CC);
if(ZEND_CE_FILENAME(ce)) {
/* do not free! PHP has centralized filename storage, compiler will free it */
ZEND_CE_FILENAME(ce) = zend_accel_memdup(ZEND_CE_FILENAME(ce), strlen(ZEND_CE_FILENAME(ce)) + 1);
}
if(ZEND_CE_DOC_COMMENT(ce)) {
if (ZCG(accel_directives).save_comments) {
zend_accel_store(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce) + 1);
} else {
if(!zend_shared_alloc_get_xlat_entry(ZEND_CE_DOC_COMMENT(ce))) {
zend_shared_alloc_register_xlat_entry(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT(ce));
efree((char*)ZEND_CE_DOC_COMMENT(ce));
}
ZEND_CE_DOC_COMMENT(ce) = NULL;
ZEND_CE_DOC_COMMENT_LEN(ce) = 0;
}
}
zend_hash_persist(&ce->properties_info, (zend_persist_func_t) zend_persist_property_info, sizeof(zend_property_info) TSRMLS_CC);
if(ce->num_interfaces && ce->interfaces) {
efree(ce->interfaces);
}
ce->interfaces = NULL; /* will be filled in on fetch */
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
if (ce->num_traits && ce->traits) {
efree(ce->traits);
}
ce->traits = NULL;
if (ce->trait_aliases) {
int i = 0;
while (ce->trait_aliases[i]) {
if (ce->trait_aliases[i]->trait_method) {
if (ce->trait_aliases[i]->trait_method->method_name) {
zend_accel_store(ce->trait_aliases[i]->trait_method->method_name,
ce->trait_aliases[i]->trait_method->mname_len+1);
}
if (ce->trait_aliases[i]->trait_method->class_name) {
zend_accel_store(ce->trait_aliases[i]->trait_method->class_name,
ce->trait_aliases[i]->trait_method->cname_len+1);
}
ce->trait_aliases[i]->trait_method->ce = NULL;
zend_accel_store(ce->trait_aliases[i]->trait_method,
sizeof(zend_trait_method_reference));
}
if (ce->trait_aliases[i]->alias) {
zend_accel_store(ce->trait_aliases[i]->alias,
ce->trait_aliases[i]->alias_len+1);
}
#if ZEND_EXTENSION_API_NO <= PHP_5_4_X_API_NO
ce->trait_aliases[i]->function = NULL;
#endif
zend_accel_store(ce->trait_aliases[i], sizeof(zend_trait_alias));
i++;
}
zend_accel_store(ce->trait_aliases, sizeof(zend_trait_alias*) * (i + 1));
}
if (ce->trait_precedences) {
int i = 0;
while (ce->trait_precedences[i]) {
zend_accel_store(ce->trait_precedences[i]->trait_method->method_name,
ce->trait_precedences[i]->trait_method->mname_len+1);
zend_accel_store(ce->trait_precedences[i]->trait_method->class_name,
ce->trait_precedences[i]->trait_method->cname_len+1);
ce->trait_precedences[i]->trait_method->ce = NULL;
zend_accel_store(ce->trait_precedences[i]->trait_method,
sizeof(zend_trait_method_reference));
if (ce->trait_precedences[i]->exclude_from_classes) {
int j = 0;
while (ce->trait_precedences[i]->exclude_from_classes[j]) {
zend_accel_store(ce->trait_precedences[i]->exclude_from_classes[j],
strlen((char*)ce->trait_precedences[i]->exclude_from_classes[j]) + 1);
j++;
}
zend_accel_store(ce->trait_precedences[i]->exclude_from_classes,
sizeof(zend_class_entry*) * (j+1));
}
#if ZEND_EXTENSION_API_NO <= PHP_5_4_X_API_NO
ce->trait_precedences[i]->function = NULL;
#endif
zend_accel_store(ce->trait_precedences[i], sizeof(zend_trait_precedence));
i++;
}
zend_accel_store(
ce->trait_precedences, sizeof(zend_trait_precedence*) * (i+1));
}
#endif
}
}
static int zend_update_property_info_ce(zend_property_info *prop TSRMLS_DC)
{
prop->ce = zend_shared_alloc_get_xlat_entry(prop->ce);
return 0;
}
static int zend_update_parent_ce(zend_class_entry **pce TSRMLS_DC)
{
zend_class_entry *ce = *pce;
if (ce->parent) {
ce->parent = zend_shared_alloc_get_xlat_entry(ce->parent);
/* We use refcount to show if the class is used as a parent */
ce->parent->refcount++;
}
/* update methods */
if(ce->constructor) {
ce->constructor = zend_shared_alloc_get_xlat_entry(ce->constructor);
/* we use refcount to show that op_array is referenced from several places */
ce->constructor->op_array.refcount++;
}
if(ce->destructor) {
ce->destructor = zend_shared_alloc_get_xlat_entry(ce->destructor);
ce->destructor->op_array.refcount++;
}
if(ce->clone) {
ce->clone = zend_shared_alloc_get_xlat_entry(ce->clone);
ce->clone->op_array.refcount++;
}
if(ce->__get) {
ce->__get = zend_shared_alloc_get_xlat_entry(ce->__get);
ce->__get->op_array.refcount++;
}
if(ce->__set) {
ce->__set = zend_shared_alloc_get_xlat_entry(ce->__set);
ce->__set->op_array.refcount++;
}
if(ce->__call) {
ce->__call = zend_shared_alloc_get_xlat_entry(ce->__call);
ce->__call->op_array.refcount++;
}
if(ce->serialize_func) {
ce->serialize_func = zend_shared_alloc_get_xlat_entry(ce->serialize_func);
ce->serialize_func->op_array.refcount++;
}
if(ce->unserialize_func) {
ce->unserialize_func = zend_shared_alloc_get_xlat_entry(ce->unserialize_func);
ce->unserialize_func->op_array.refcount++;
}
if(ce->__isset) {
ce->__isset = zend_shared_alloc_get_xlat_entry(ce->__isset);
ce->__isset->op_array.refcount++;
}
if(ce->__unset) {
ce->__unset = zend_shared_alloc_get_xlat_entry(ce->__unset);
ce->__unset->op_array.refcount++;
}
if(ce->__tostring) {
ce->__tostring = zend_shared_alloc_get_xlat_entry(ce->__tostring);
ce->__tostring->op_array.refcount++;
}
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
if(ce->__callstatic) {
ce->__callstatic = zend_shared_alloc_get_xlat_entry(ce->__callstatic);
ce->__callstatic->op_array.refcount++;
}
#endif
zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce TSRMLS_CC);
return 0;
}
static void zend_accel_persist_class_table(HashTable *class_table TSRMLS_DC)
{
zend_hash_persist(class_table, (zend_persist_func_t) zend_persist_class_entry, sizeof(zend_class_entry*) TSRMLS_CC);
zend_hash_apply(class_table, (apply_func_t) zend_update_parent_ce TSRMLS_CC);
}
zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, char **key, unsigned int key_length TSRMLS_DC)
{
zend_shared_alloc_clear_xlat_table();
zend_hash_persist(&script->function_table, (zend_persist_func_t) zend_persist_op_array, sizeof(zend_op_array) TSRMLS_CC);
zend_accel_persist_class_table(&script->class_table TSRMLS_CC);
zend_persist_op_array_ex(&script->main_op_array, script TSRMLS_CC);
*key = zend_accel_memdup(*key, key_length + 1);
zend_accel_store(script->full_path, script->full_path_len + 1);
zend_accel_store(script, sizeof(zend_persistent_script));
return script;
}
int zend_accel_script_persistable(zend_persistent_script *script)
{
return 1;
}

29
zend_persist.h

@ -0,0 +1,29 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_PERSIST_H
#define ZEND_PERSIST_H
int zend_accel_script_persistable(zend_persistent_script *script);
uint zend_accel_script_persist_calc(zend_persistent_script *script, char *key, unsigned int key_length TSRMLS_DC);
zend_persistent_script *zend_accel_script_persist(zend_persistent_script *script, char **key, unsigned int key_length TSRMLS_DC);
#endif /* ZEND_PERSIST_H */

343
zend_persist_calc.c

@ -0,0 +1,343 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include "zend.h"
#include "ZendAccelerator.h"
#include "zend_persist.h"
#include "zend_extensions.h"
#include "zend_shared_alloc.h"
#include "zend_operators.h"
#define START_SIZE() uint memory_used = 0
#define ADD_DUP_SIZE(m,s) memory_used += zend_shared_memdup_size((void*)m, s)
#define ADD_SIZE(m) memory_used += ZEND_ALIGNED_SIZE(m)
#define RETURN_SIZE() return memory_used
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
# define ADD_INTERNED_STRING(str, len) do { \
const char *tmp = accel_new_interned_string((str), (len), !IS_INTERNED((str)) TSRMLS_CC); \
if (tmp != (str)) { \
(str) = (char*)tmp; \
} else { \
ADD_DUP_SIZE((str), (len)); \
} \
} while (0)
#else
# define ADD_INTERNED_STRING(str, len) ADD_DUP_SIZE((str), (len))
#endif
static uint zend_persist_zval_ptr_calc(zval **zp TSRMLS_DC);
static uint zend_hash_persist_calc(HashTable *ht, int (*pPersistElement)(void *pElement TSRMLS_DC), size_t el_size TSRMLS_DC)
{
Bucket *p = ht->pListHead;
START_SIZE();
while (p) {
/* persist bucket and key */
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
ADD_DUP_SIZE(p, sizeof(Bucket));
if (p->nKeyLength) {
const char *tmp = accel_new_interned_string(p->arKey, p->nKeyLength, 0 TSRMLS_CC);
if (tmp != p->arKey) {
p->arKey = tmp;
} else {
ADD_DUP_SIZE(p->arKey, p->nKeyLength);
}
}
#else
ADD_DUP_SIZE(p, sizeof(Bucket) - 1 + p->nKeyLength);
#endif
/* persist data pointer in bucket */
if (!p->pDataPtr) {
ADD_DUP_SIZE(p->pData, el_size);
}
/* persist the data itself */
if (pPersistElement) {
ADD_SIZE(pPersistElement(p->pData TSRMLS_CC));
}
p = p->pListNext;
}
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
if (ht->nTableMask) {
ADD_DUP_SIZE(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize);
}
#else
ADD_DUP_SIZE(ht->arBuckets, sizeof(Bucket*) * ht->nTableSize);
#endif
RETURN_SIZE();
}
static uint zend_persist_zval_calc(zval *z TSRMLS_DC)
{
START_SIZE();
#if ZEND_EXTENSION_API_NO >= PHP_5_3_X_API_NO
switch (z->type & IS_CONSTANT_TYPE_MASK) {
#else
switch (z->type & ~IS_CONSTANT_INDEX) {
#endif
case IS_STRING:
case IS_CONSTANT:
ADD_INTERNED_STRING(Z_STRVAL_P(z), Z_STRLEN_P(z) + 1);
break;
case IS_ARRAY:
case IS_CONSTANT_ARRAY:
ADD_DUP_SIZE(z->value.ht, sizeof(HashTable));
ADD_SIZE(zend_hash_persist_calc(z->value.ht, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
break;
}
RETURN_SIZE();
}
static uint zend_persist_zval_ptr_calc(zval **zp TSRMLS_DC)
{
START_SIZE();
zval *new_ptr = zend_shared_alloc_get_xlat_entry(*zp);
if (!new_ptr) {
ADD_DUP_SIZE(*zp, sizeof(zval));
ADD_SIZE(zend_persist_zval_calc(*zp TSRMLS_CC));
}
RETURN_SIZE();
}
static uint zend_persist_op_array_calc(zend_op_array *op_array TSRMLS_DC)
{
START_SIZE();
if (op_array->type != ZEND_USER_FUNCTION) {
return 0;
}
if (op_array->filename) {
ADD_DUP_SIZE(op_array->filename, strlen(op_array->filename) + 1);
}
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
if (op_array->literals && !zend_shared_alloc_get_xlat_entry(op_array->literals)) {
zend_literal *p = op_array->literals;
zend_literal *end = p + op_array->last_literal;
ADD_DUP_SIZE(op_array->literals, sizeof(zend_literal) * op_array->last_literal);
while (p < end) {
ADD_SIZE(zend_persist_zval_calc(&p->constant TSRMLS_CC));
p++;
}
}
#endif
if (!zend_shared_alloc_get_xlat_entry(op_array->opcodes)) {
#if ZEND_EXTENSION_API_NO <= PHP_5_3_X_API_NO
zend_op *opline = op_array->opcodes;
zend_op *end = op_array->opcodes+op_array->last;
ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last);
while (opline<end) {
if (opline->op1.op_type==IS_CONST) {
ADD_SIZE(zend_persist_zval_calc(&opline->op1.u.constant TSRMLS_CC));
}
if (opline->op2.op_type==IS_CONST) {
ADD_SIZE(zend_persist_zval_calc(&opline->op2.u.constant TSRMLS_CC));
}
opline++;
}
#else
ADD_DUP_SIZE(op_array->opcodes, sizeof(zend_op) * op_array->last);
#endif
}
if (op_array->function_name) {
ADD_DUP_SIZE(op_array->function_name, strlen(op_array->function_name) + 1);
}
if (op_array->arg_info &&
!zend_shared_alloc_get_xlat_entry(op_array->arg_info)) {
zend_uint i;
ADD_DUP_SIZE(op_array->arg_info, sizeof(zend_arg_info) * op_array->num_args);
for(i=0;i<op_array->num_args;i++) {
if(op_array->arg_info[i].name) {
ADD_INTERNED_STRING(op_array->arg_info[i].name, op_array->arg_info[i].name_len + 1);
}
if(op_array->arg_info[i].class_name) {
ADD_INTERNED_STRING(op_array->arg_info[i].class_name, op_array->arg_info[i].class_name_len + 1);
}
}
}
if (op_array->brk_cont_array) {
ADD_DUP_SIZE(op_array->brk_cont_array, sizeof(zend_brk_cont_element) * op_array->last_brk_cont);
}
if (op_array->static_variables) {
ADD_DUP_SIZE(op_array->static_variables, sizeof(HashTable));
ADD_SIZE(zend_hash_persist_calc(op_array->static_variables, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
}
if(ZCG(accel_directives).save_comments && op_array->doc_comment) {
ADD_DUP_SIZE(op_array->doc_comment, op_array->doc_comment_len + 1);
}
if(op_array->try_catch_array) {
ADD_DUP_SIZE(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
}
if(op_array->vars && !zend_shared_alloc_get_xlat_entry(op_array->vars)) {
int i;
ADD_DUP_SIZE(op_array->vars, sizeof(zend_compiled_variable) * op_array->last_var);
for(i=0; i<op_array->last_var; i++) {
ADD_INTERNED_STRING(op_array->vars[i].name, op_array->vars[i].name_len + 1);
}
}
RETURN_SIZE();
}
static uint zend_persist_property_info_calc(zend_property_info *prop TSRMLS_DC)
{
START_SIZE();
ADD_INTERNED_STRING(prop->name, prop->name_length + 1);
if(ZCG(accel_directives).save_comments && prop->doc_comment) {
ADD_DUP_SIZE(prop->doc_comment, prop->doc_comment_len + 1);
}
RETURN_SIZE();
}
static uint zend_persist_class_entry_calc(zend_class_entry **pce TSRMLS_DC)
{
zend_class_entry *ce = *pce;
START_SIZE();
if (ce->type == ZEND_USER_CLASS) {
ADD_DUP_SIZE(ce, sizeof(zend_class_entry));
ADD_INTERNED_STRING(ce->name, ce->name_length + 1);
ADD_SIZE(zend_hash_persist_calc(&ce->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC));
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
if (ce->default_properties_table) {
int i;
ADD_SIZE(sizeof(zval*) * ce->default_properties_count);
for (i = 0; i < ce->default_properties_count; i++) {
if (ce->default_properties_table[i]) {
ADD_SIZE(zend_persist_zval_ptr_calc(&ce->default_properties_table[i] TSRMLS_CC));
}
}
}
if (ce->default_static_members_table) {
int i;
ADD_SIZE(sizeof(zval*) * ce->default_static_members_count);
for (i = 0; i < ce->default_static_members_count; i++) {
if (ce->default_static_members_table[i]) {
ADD_SIZE(zend_persist_zval_ptr_calc(&ce->default_static_members_table[i] TSRMLS_CC));
}
}
}
#else
ADD_SIZE(zend_hash_persist_calc(&ce->default_properties, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
ADD_SIZE(zend_hash_persist_calc(&ce->default_static_members, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
#endif
ADD_SIZE(zend_hash_persist_calc(&ce->constants_table, (int (*)(void* TSRMLS_DC)) zend_persist_zval_ptr_calc, sizeof(zval**) TSRMLS_CC));
if (ZEND_CE_FILENAME(ce)) {
ADD_DUP_SIZE(ZEND_CE_FILENAME(ce), strlen(ZEND_CE_FILENAME(ce)) + 1);
}
if(ZCG(accel_directives).save_comments && ZEND_CE_DOC_COMMENT(ce)) {
ADD_DUP_SIZE(ZEND_CE_DOC_COMMENT(ce), ZEND_CE_DOC_COMMENT_LEN(ce) + 1);
}
ADD_SIZE(zend_hash_persist_calc(&ce->properties_info, (int (*)(void* TSRMLS_DC)) zend_persist_property_info_calc, sizeof(zend_property_info) TSRMLS_CC));
#if ZEND_EXTENSION_API_NO > PHP_5_3_X_API_NO
if (ce->trait_aliases) {
int i = 0;
while (ce->trait_aliases[i]) {
if (ce->trait_aliases[i]->trait_method) {
if (ce->trait_aliases[i]->trait_method->method_name) {
ADD_SIZE(ce->trait_aliases[i]->trait_method->mname_len+1);
}
if (ce->trait_aliases[i]->trait_method->class_name) {
ADD_SIZE(ce->trait_aliases[i]->trait_method->cname_len+1);
}
ADD_SIZE(sizeof(zend_trait_method_reference));
}
if (ce->trait_aliases[i]->alias) {
ADD_SIZE(ce->trait_aliases[i]->alias_len+1);
}
ADD_SIZE(sizeof(zend_trait_alias));
i++;
}
ADD_SIZE(sizeof(zend_trait_alias*) * (i + 1));
}
if (ce->trait_precedences) {
int i = 0;
while (ce->trait_precedences[i]) {
ADD_SIZE(ce->trait_precedences[i]->trait_method->mname_len+1);
ADD_SIZE(ce->trait_precedences[i]->trait_method->cname_len+1);
ADD_SIZE(sizeof(zend_trait_method_reference));
if (ce->trait_precedences[i]->exclude_from_classes) {
int j = 0;
while (ce->trait_precedences[i]->exclude_from_classes[j]) {
ADD_SIZE(strlen((char*)ce->trait_precedences[i]->exclude_from_classes[j]) + 1);
j++;
}
ADD_SIZE(sizeof(zend_class_entry*) * (j+1));
}
ADD_SIZE(sizeof(zend_trait_precedence));
i++;
}
ADD_SIZE(sizeof(zend_trait_precedence*) * (i+1));
}
#endif
}
RETURN_SIZE();
}
static uint zend_accel_persist_class_table_calc(HashTable *class_table TSRMLS_DC)
{
return zend_hash_persist_calc(class_table, (int (*)(void* TSRMLS_DC)) zend_persist_class_entry_calc, sizeof(zend_class_entry*) TSRMLS_CC);
}
uint zend_accel_script_persist_calc(zend_persistent_script *new_persistent_script, char *key, unsigned int key_length TSRMLS_DC)
{
START_SIZE();
ADD_SIZE(zend_hash_persist_calc(&new_persistent_script->function_table, (int (*)(void* TSRMLS_DC)) zend_persist_op_array_calc, sizeof(zend_op_array) TSRMLS_CC));
ADD_SIZE(zend_accel_persist_class_table_calc(&new_persistent_script->class_table TSRMLS_CC));
ADD_SIZE(zend_persist_op_array_calc(&new_persistent_script->main_op_array TSRMLS_CC));
ADD_DUP_SIZE(key, key_length + 1);
ADD_DUP_SIZE(new_persistent_script->full_path, new_persistent_script->full_path_len + 1);
ADD_DUP_SIZE(new_persistent_script, sizeof(zend_persistent_script));
RETURN_SIZE();
}

473
zend_shared_alloc.c

@ -0,0 +1,473 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#include <errno.h>
#include "ZendAccelerator.h"
#include "zend_shared_alloc.h"
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <fcntl.h>
#ifndef ZEND_WIN32
# include <sys/types.h>
# include <dirent.h>
# include <signal.h>
# include <sys/stat.h>
# include <stdio.h>
#endif
#ifdef HAVE_MPROTECT
# include "sys/mman.h"
#endif
#define TMP_DIR "/tmp"
#define SEM_FILENAME_PREFIX ".ZendSem."
#define S_H(s) g_shared_alloc_handler->s
/* True globals */
static zend_bool locked;
/* old/new mapping. We can use true global even for ZTS because its usage
is wrapped with exclusive lock anyway */
static HashTable xlat_table;
static const zend_shared_memory_handlers *g_shared_alloc_handler = NULL;
static const char *g_shared_model;
/* pointer to globals allocated in SHM and shared across processes */
zend_smm_shared_globals *smm_shared_globals;
#ifndef ZEND_WIN32
int lock_file;
static char lockfile_name[sizeof(TMP_DIR)+sizeof(SEM_FILENAME_PREFIX)+8];
#endif
static const zend_shared_memory_handler_entry handler_table[] = {
#if USE_MMAP
{ "mmap", &zend_alloc_mmap_handlers },
#endif
#if USE_SHM
{ "shm", &zend_alloc_shm_handlers },
#endif
#if USE_SHM_OPEN
{ "posix", &zend_alloc_posix_handlers },
#endif
#ifdef ZEND_WIN32
{ "win32", &zend_alloc_win32_handlers },
#endif
{ NULL, NULL}
};
#ifndef ZEND_WIN32
void zend_shared_alloc_create_lock(void)
{
int val;
sprintf(lockfile_name, "%s/%sXXXXXX", TMP_DIR, SEM_FILENAME_PREFIX);
lock_file = mkstemp(lockfile_name);
fchmod(lock_file, 0666);
if (lock_file == -1) {
zend_accel_error(ACCEL_LOG_FATAL, "Unable to create lock file: %s (%d)", strerror(errno), errno);
}
val = fcntl(lock_file, F_GETFD, 0);
val |= FD_CLOEXEC;
fcntl(lock_file, F_SETFD, val);
unlink(lockfile_name);
}
#endif
static void no_memory_bailout(int allocate_size, char *error)
{
zend_accel_error(ACCEL_LOG_FATAL, "Unable to allocate shared memory segment of %d bytes: %s: %s (%d)", allocate_size, error?error:"unknown", strerror(errno), errno );
}
static void copy_shared_segments(void *to, void *from, int count, int size)
{
zend_shared_segment **shared_segments_v = (zend_shared_segment **)to;
void *shared_segments_to_p = ((char *)to + count*(sizeof(void *)));
void *shared_segments_from_p = from;
int i;
for(i=0;i<count;i++) {
shared_segments_v[i] = shared_segments_to_p;
memcpy(shared_segments_to_p, shared_segments_from_p, size);
shared_segments_to_p = ((char *)shared_segments_to_p + size);
shared_segments_from_p = ((char *)shared_segments_from_p + size);
}
}
static int zend_shared_alloc_try(const zend_shared_memory_handler_entry *he, int requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
{
int res;
g_shared_alloc_handler = he->handler;
g_shared_model = he->name;
ZSMMG(shared_segments) = NULL;
ZSMMG(shared_segments_count) = 0;
res = S_H(create_segments)(requested_size, shared_segments_p, shared_segments_count, error_in);
if( res ) {
/* this model works! */
return res;
}
if(*shared_segments_p) {
int i;
/* cleanup */
for(i=0;i<*shared_segments_count;i++) {
if((*shared_segments_p)[i]->p && (int)(*shared_segments_p)[i]->p != -1) {
S_H(detach_segment)((*shared_segments_p)[i]);
}
}
free(*shared_segments_p);
*shared_segments_p = NULL;
}
g_shared_alloc_handler = NULL;
return ALLOC_FAILURE;
}
int zend_shared_alloc_startup(int requested_size)
{
zend_shared_segment **tmp_shared_segments;
size_t shared_segments_array_size;
zend_smm_shared_globals tmp_shared_globals, *p_tmp_shared_globals;
char *error_in = NULL;
const zend_shared_memory_handler_entry *he;
int res = ALLOC_FAILURE;
TSRMLS_FETCH();
/* shared_free must be valid before we call zend_shared_alloc()
* - make it temporarily point to a local variable
*/
smm_shared_globals = &tmp_shared_globals;
ZSMMG(shared_free) = requested_size; /* goes to tmp_shared_globals.shared_free */
zend_shared_alloc_create_lock();
if(ZCG(accel_directives).memory_model && ZCG(accel_directives).memory_model[0]) {
char* model = ZCG(accel_directives).memory_model;
/* "cgi" is really "shm"... */
if( strncmp(ZCG(accel_directives).memory_model,"cgi",4) == 0 ){
model = "shm";
}
for(he = handler_table; he->name; he++) {
if(strcmp(model, he->name) == 0) {
res=zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
if( res ) {
/* this model works! */
}
break;
}
}
}
if( res == FAILED_REATTACHED ){
smm_shared_globals = NULL;
return res;
}
if(!g_shared_alloc_handler) {
/* try memory handlers in order */
for(he = handler_table; he->name; he++) {
res=zend_shared_alloc_try(he, requested_size, &ZSMMG(shared_segments), &ZSMMG(shared_segments_count), &error_in);
if( res ) {
/* this model works! */
break;
}
}
}
if(!g_shared_alloc_handler) {
no_memory_bailout(requested_size, error_in);
return ALLOC_FAILURE;
}
if( res == SUCCESSFULLY_REATTACHED ){
return res;
}
shared_segments_array_size = ZSMMG(shared_segments_count)*S_H(segment_type_size)();
/* move shared_segments and shared_free to shared memory */
locked = 1; /* no need to perform a real lock at this point */
p_tmp_shared_globals = (zend_smm_shared_globals *) zend_shared_alloc(sizeof(zend_smm_shared_globals));
tmp_shared_segments = zend_shared_alloc(shared_segments_array_size+ZSMMG(shared_segments_count)*sizeof(void *));
copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
*p_tmp_shared_globals = tmp_shared_globals;
smm_shared_globals = p_tmp_shared_globals;
free(ZSMMG(shared_segments));
ZSMMG(shared_segments) = tmp_shared_segments;
ZSMMG(shared_memory_state).positions = (int *) zend_shared_alloc(sizeof(int)*ZSMMG(shared_segments_count));
locked = 0;
return res;
}
void zend_shared_alloc_shutdown(void)
{
zend_shared_segment **tmp_shared_segments;
size_t shared_segments_array_size;
zend_smm_shared_globals tmp_shared_globals;
int i;
tmp_shared_globals = *smm_shared_globals;
smm_shared_globals = &tmp_shared_globals;
shared_segments_array_size = ZSMMG(shared_segments_count)*(S_H(segment_type_size)()+sizeof(void *));
tmp_shared_segments = emalloc(shared_segments_array_size);
copy_shared_segments(tmp_shared_segments, ZSMMG(shared_segments)[0], ZSMMG(shared_segments_count), S_H(segment_type_size)());
ZSMMG(shared_segments) = tmp_shared_segments;
for(i=0; i<ZSMMG(shared_segments_count); i++) {
S_H(detach_segment)(ZSMMG(shared_segments)[i]);
}
efree(ZSMMG(shared_segments));
ZSMMG(shared_segments) = NULL;
g_shared_alloc_handler = NULL;
#ifndef ZEND_WIN32
close(lock_file);
#endif
locked = 0;
}
#define SHARED_ALLOC_FAILED() { \
TSRMLS_FETCH(); \
\
zend_accel_error(ACCEL_LOG_WARNING, "Not enough free shared space to allocate %ld bytes (%ld bytes free)", (long)size, (long)ZSMMG(shared_free)); \
ZSMMG(memory_exhausted) = 1; \
zend_accel_schedule_restart(TSRMLS_C); \
}
void *zend_shared_alloc(size_t size)
{
int i;
unsigned int block_size = size+sizeof(zend_shared_memory_block_header);
#if 1
if (!locked) {
zend_accel_error(ACCEL_LOG_ERROR, "Shared memory lock not obtained");
}
#endif
if (block_size > ZSMMG(shared_free)) { /* No hope to find a big-enough block */
SHARED_ALLOC_FAILED();
return NULL;
}
for (i=0; i<ZSMMG(shared_segments_count); i++) {
if (ZSMMG(shared_segments)[i]->size-ZSMMG(shared_segments)[i]->pos >= block_size) { /* found a valid block */
zend_shared_memory_block_header *p = (zend_shared_memory_block_header *) (((char *) ZSMMG(shared_segments)[i]->p)+ZSMMG(shared_segments)[i]->pos);
int remainder = block_size % PLATFORM_ALIGNMENT;
void *retval;
if (remainder!=0) {
size += PLATFORM_ALIGNMENT-remainder;
block_size += PLATFORM_ALIGNMENT-remainder;
}
ZSMMG(shared_segments)[i]->pos += block_size;
ZSMMG(shared_free) -= block_size;
p->size = size;
retval = ((char *) p)+sizeof(zend_shared_memory_block_header);
memset(retval, 0, size);
return retval;
}
}
SHARED_ALLOC_FAILED();
return NULL;
}
int zend_shared_memdup_size(void *source, size_t size)
{
void **old_p;
if (zend_hash_index_find(&xlat_table, (ulong) source, (void **) &old_p)==SUCCESS) {
/* we already duplicated this pointer */
return 0;
}
zend_shared_alloc_register_xlat_entry(source, source);
return ZEND_ALIGNED_SIZE(size);
}
void *_zend_shared_memdup(void *source, size_t size, zend_bool free_source TSRMLS_DC)
{
void **old_p, *retval;
if (zend_hash_index_find(&xlat_table, (ulong) source, (void **) &old_p)==SUCCESS) {
/* we already duplicated this pointer */
return *old_p;
}
retval = ZCG(mem);;
ZCG(mem) = (void*)(((char*)ZCG(mem)) + ZEND_ALIGNED_SIZE(size));
memcpy(retval, source, size);
if (free_source) {
interned_efree((char*)source);
}
zend_shared_alloc_register_xlat_entry(source, retval);
return retval;
}
void zend_shared_alloc_safe_unlock(TSRMLS_D)
{
if (locked) {
zend_shared_alloc_unlock(TSRMLS_C);
}
}
#ifndef ZEND_WIN32
/* name l_type l_whence l_start l_len */
static FLOCK_STRUCTURE(mem_write_lock, F_WRLCK, SEEK_SET, 0, 1);
static FLOCK_STRUCTURE(mem_write_unlock, F_UNLCK, SEEK_SET, 0, 1);
#endif
void zend_shared_alloc_lock(TSRMLS_D)
{
#ifndef ZEND_WIN32
#if 0
/* this will happen once per process, and will un-globalize mem_write_lock */
if (mem_write_lock.l_pid == -1) {
mem_write_lock.l_pid = getpid();
}
#endif
while (1) {
if (fcntl(lock_file, F_SETLKW, &mem_write_lock) == -1) {
if (errno == EINTR) {
continue;
}
zend_accel_error(ACCEL_LOG_ERROR, "Cannot create lock - %s (%d)", strerror(errno), errno);
}
break;
}
#else
zend_shared_alloc_lock_win32();
#endif
locked=1;
/* Prepare translation table
*
* Make it persistent so that it uses malloc() and allocated blocks
* won't be taken from space which is freed by efree in memdup.
* Otherwise it leads to false matches in memdup check.
*/
zend_hash_init(&xlat_table, 100, NULL, NULL, 1);
}
void zend_shared_alloc_unlock(TSRMLS_D)
{
/* Destroy translation table */
zend_hash_destroy(&xlat_table);
locked=0;
#ifndef ZEND_WIN32
if (fcntl(lock_file, F_SETLK, &mem_write_unlock) == -1) {
zend_accel_error(ACCEL_LOG_ERROR, "Cannot remove lock - %s (%d)", strerror(errno), errno);
}
#else
zend_shared_alloc_unlock_win32();
#endif
}
void zend_shared_alloc_clear_xlat_table(void)
{
zend_hash_clean(&xlat_table);
}
void zend_shared_alloc_register_xlat_entry(const void *old, const void *new)
{
zend_hash_index_update(&xlat_table, (ulong) old, (void*)&new, sizeof(void *), NULL);
}
void *zend_shared_alloc_get_xlat_entry(const void *old)
{
void **retval;
if (zend_hash_index_find(&xlat_table, (ulong) old, (void **) &retval)==FAILURE) {
return NULL;
}
return *retval;
}
size_t zend_shared_alloc_get_free_memory(void)
{
return ZSMMG(shared_free);
}
size_t zend_shared_alloc_get_largest_free_block(void)
{
int i;
size_t largest_block_size=0;
for (i=0; i<ZSMMG(shared_segments_count); i++) {
size_t block_size = ZSMMG(shared_segments)[i]->size - ZSMMG(shared_segments)[i]->pos;
if (block_size>largest_block_size) {
largest_block_size = block_size;
}
}
return largest_block_size;
}
void zend_shared_alloc_save_state(void)
{
int i;
for (i=0; i<ZSMMG(shared_segments_count); i++) {
ZSMMG(shared_memory_state).positions[i] = ZSMMG(shared_segments)[i]->pos;
}
ZSMMG(shared_memory_state).shared_free = ZSMMG(shared_free);
}
void zend_shared_alloc_restore_state(void)
{
int i;
for (i=0; i<ZSMMG(shared_segments_count); i++) {
ZSMMG(shared_segments)[i]->pos = ZSMMG(shared_memory_state).positions[i];
}
ZSMMG(shared_free) = ZSMMG(shared_memory_state).shared_free;
ZSMMG(memory_exhausted) = 0;
ZSMMG(wasted_shared_memory) = 0;
}
const char *zend_accel_get_shared_model()
{
return g_shared_model;
}
void zend_accel_shared_protect(int mode TSRMLS_DC)
{
#ifdef HAVE_MPROTECT
int i;
if (mode) {
mode = PROT_READ;
} else {
mode = PROT_READ|PROT_WRITE;
}
for(i=0; i < ZSMMG(shared_segments_count); i++) {
mprotect(ZSMMG(shared_segments)[i]->p, ZSMMG(shared_segments)[i]->size, mode);
}
#endif
}

166
zend_shared_alloc.h

@ -0,0 +1,166 @@
/*
+----------------------------------------------------------------------+
| Zend Optimizer+ |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2013 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: Andi Gutmans <andi@zend.com> |
| Zeev Suraski <zeev@zend.com> |
| Stanislav Malyshev <stas@zend.com> |
| Dmitry Stogov <dmitry@zend.com> |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_SHARED_ALLOC_H
#define ZEND_SHARED_ALLOC_H
#include "zend.h"
#if defined(__APPLE__) && defined(__MACH__) /* darwin */
# define USE_SHM_OPEN 1
# define USE_MMAP 1
#elif defined(__linux__) || defined(_AIX)
# define USE_SHM 1
# define USE_MMAP 1
#elif defined(__FreeBSD__)
# define USE_SHM_OPEN 1
# define USE_MMAP 1
# define USE_SHM 1
#elif defined(__sparc) || defined(__sun)
# define USE_SHM_OPEN 1
# define USE_SHM 1
# if defined(__i386)
# define USE_MMAP 1
# endif
#endif
#define ALLOC_FAILURE 0
#define ALLOC_SUCCESS 1
#define FAILED_REATTACHED 2
#define SUCCESSFULLY_REATTACHED 4
#define ALLOC_FAIL_MAPPING 8
typedef struct _zend_shared_segment {
size_t size;
size_t pos; /* position for simple stack allocator */
void *p;
} zend_shared_segment;
typedef int (*create_segments_t)(size_t requested_size, zend_shared_segment ***shared_segments, int *shared_segment_count, char **error_in);
typedef int (*detach_segment_t)(zend_shared_segment *shared_segment);
typedef struct {
create_segments_t create_segments;
detach_segment_t detach_segment;
size_t (*segment_type_size)(void);
} zend_shared_memory_handlers;
typedef struct _handler_entry {
const char *name;
zend_shared_memory_handlers *handler;
} zend_shared_memory_handler_entry;
typedef struct _zend_shared_memory_block_header {
int size;
} zend_shared_memory_block_header;
typedef struct _zend_shared_memory_state {
int *positions; /* current positions for each segment */
int shared_free; /* amount of free shared memory */
} zend_shared_memory_state;
typedef struct _zend_smm_shared_globals {
/* Shared Memory Manager */
zend_shared_segment **shared_segments;
/* Number of allocated shared segments */
int shared_segments_count;
/* Amount of free shared memory */
size_t shared_free;
/* Amount of shared memory allocated by garbage */
int wasted_shared_memory;
/* No more shared memory flag */
zend_bool memory_exhausted;
/* Saved Shared Allocator State */
zend_shared_memory_state shared_memory_state;
/* Pointer to the application's shared data structures */
void *app_shared_globals;
} zend_smm_shared_globals;
extern zend_smm_shared_globals *smm_shared_globals;
#define ZSMMG(element) (smm_shared_globals->element)
#define SHARED_ALLOC_REATTACHED (SUCCESS+1)
int zend_shared_alloc_startup(int requested_size);
void zend_shared_alloc_shutdown(void);
/* allocate shared memory block */
void *zend_shared_alloc(size_t size);
/* copy into shared memory */
void *_zend_shared_memdup(void *p, size_t size, zend_bool free_source TSRMLS_DC);
int zend_shared_memdup_size(void *p, size_t size);
typedef union _align_test {
void *ptr;
double dbl;
long lng;
} align_test;
#if ZEND_GCC_VERSION >= 2000
# define PLATFORM_ALIGNMENT (__alignof__ (align_test))
#else
# define PLATFORM_ALIGNMENT (sizeof(align_test))
#endif
#define ZEND_ALIGNED_SIZE(size) \
((size + PLATFORM_ALIGNMENT - 1) & ~(PLATFORM_ALIGNMENT - 1))
/* exclusive locking */
void zend_shared_alloc_lock(TSRMLS_D);
void zend_shared_alloc_unlock(TSRMLS_D); /* returns the allocated size during lock..unlock */
void zend_shared_alloc_safe_unlock(TSRMLS_D);
/* old/new mapping functions */
void zend_shared_alloc_clear_xlat_table(void);
void zend_shared_alloc_register_xlat_entry(const void *old, const void *new);
void *zend_shared_alloc_get_xlat_entry(const void *old);
size_t zend_shared_alloc_get_free_memory(void);
void zend_shared_alloc_save_state(void);
void zend_shared_alloc_restore_state(void);
size_t zend_shared_alloc_get_largest_free_block(void);
const char *zend_accel_get_shared_model();
/* memory write protection */
void zend_accel_shared_protect(int mode TSRMLS_DC);
#ifdef USE_MMAP
extern zend_shared_memory_handlers zend_alloc_mmap_handlers;
#endif
#ifdef USE_SHM
extern zend_shared_memory_handlers zend_alloc_shm_handlers;
#endif
#ifdef USE_SHM_OPEN
extern zend_shared_memory_handlers zend_alloc_posix_handlers;
#endif
#ifdef ZEND_WIN32
extern zend_shared_memory_handlers zend_alloc_win32_handlers;
void zend_shared_alloc_create_lock();
void zend_shared_alloc_lock_win32();
void zend_shared_alloc_unlock_win32();
#endif
#endif /* ZEND_SHARED_ALLOC_H */
Loading…
Cancel
Save