XCache is a fast, stable PHP opcode cacher that has been proven and is now running on production servers under high load. https://xcache.lighttpd.net/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1075 lines
30 KiB

  1. /* {{{ macros */
  2. #include <stdlib.h>
  3. #include <stdio.h>
  4. #include <string.h>
  5. #include <signal.h>
  6. #include "xcache.h"
  7. #ifdef HAVE_XCACHE_OPTIMIZER
  8. # include "mod_optimizer/xc_optimizer.h"
  9. #endif
  10. #ifdef HAVE_XCACHE_CACHER
  11. # include "mod_cacher/xc_cacher.h"
  12. #endif
  13. #ifdef HAVE_XCACHE_COVERAGER
  14. # include "mod_coverager/xc_coverager.h"
  15. #endif
  16. #ifdef HAVE_XCACHE_DISASSEMBLER
  17. # include "mod_disassembler/xc_disassembler.h"
  18. #endif
  19. #include "xcache_globals.h"
  20. #include "xcache/xc_extension.h"
  21. #include "xcache/xc_ini.h"
  22. #include "xcache/xc_const_string.h"
  23. #include "xcache/xc_opcode_spec.h"
  24. #include "xcache/xc_utils.h"
  25. #include "util/xc_vector.h"
  26. #include "php.h"
  27. #include "ext/standard/info.h"
  28. #include "ext/standard/php_string.h"
  29. #include "SAPI.h"
  30. /* }}} */
  31. /* {{{ globals */
  32. static char *xc_coredump_dir = NULL;
  33. #ifdef ZEND_WIN32
  34. static zend_ulong xc_coredump_type = 0;
  35. #endif
  36. static zend_bool xc_disable_on_crash = 0;
  37. static zend_compile_file_t *old_compile_file = NULL;
  38. zend_bool xc_test = 0;
  39. ZEND_DECLARE_MODULE_GLOBALS(xcache)
  40. /* }}} */
  41. static zend_op_array *xc_check_initial_compile_file(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  42. {
  43. XG(initial_compile_file_called) = 1;
  44. return old_compile_file(h, type TSRMLS_CC);
  45. }
  46. /* }}} */
  47. /* devel helper function */
  48. #if 0
  49. static void xc_list_extensions() /* {{{ */
  50. {
  51. zend_llist_element *element;
  52. zend_extension *ext;
  53. fprintf(stderr, "extensions:\n");
  54. for (element = zend_extensions.head; element; element = element->next) {
  55. ext = (zend_extension *) element->data;
  56. fprintf(stderr, " - %s\n", ext->name);
  57. }
  58. }
  59. /* }}} */
  60. #endif
  61. /* module helper function */
  62. static int xc_init_constant(int module_number TSRMLS_DC) /* {{{ */
  63. {
  64. typedef struct {
  65. const char *prefix;
  66. zend_uchar (*getsize)();
  67. const char *(*get)(zend_uchar i);
  68. } xc_nameinfo_t;
  69. xc_nameinfo_t nameinfos[] = {
  70. { "", xc_get_op_type_count, xc_get_op_type },
  71. { "", xc_get_data_type_count, xc_get_data_type },
  72. { "", xc_get_opcode_count, xc_get_opcode },
  73. { "OPSPEC_", xc_get_op_spec_count, xc_get_op_spec },
  74. { NULL, NULL, NULL }
  75. };
  76. int undefdone = 0;
  77. xc_nameinfo_t *p;
  78. for (p = nameinfos; p->getsize; p ++) {
  79. zend_uchar i, count;
  80. char const_name[96];
  81. int const_name_len;
  82. count = p->getsize();
  83. for (i = 0; i < count; i ++) {
  84. const char *name = p->get(i);
  85. if (!name) continue;
  86. if (strcmp(name, "UNDEF") == 0) {
  87. if (undefdone) continue;
  88. undefdone = 1;
  89. }
  90. const_name_len = snprintf(const_name, sizeof(const_name), "XC_%s%s", p->prefix, name);
  91. zend_register_long_constant(const_name, const_name_len+1, i, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  92. }
  93. }
  94. zend_register_long_constant(XCACHE_STRS("XC_SIZEOF_TEMP_VARIABLE"), sizeof(temp_variable), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  95. zend_register_stringl_constant(XCACHE_STRS("XCACHE_VERSION"), XCACHE_STRL(XCACHE_VERSION), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  96. zend_register_stringl_constant(XCACHE_STRS("XCACHE_MODULES"), XCACHE_STRL(XCACHE_MODULES), CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC);
  97. return 0;
  98. }
  99. /* }}} */
  100. /* {{{ PHP_GINIT_FUNCTION(xcache) */
  101. #ifdef __GNUC__
  102. #pragma GCC push_options
  103. #pragma GCC diagnostic ignored "-Wshadow"
  104. #endif
  105. #ifdef PHP_GINIT_FUNCTION
  106. static PHP_GINIT_FUNCTION(xcache)
  107. #else
  108. static void xc_init_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
  109. #endif
  110. {
  111. #ifdef __GNUC__
  112. #pragma GCC pop_options
  113. #endif
  114. memset(xcache_globals, 0, sizeof(zend_xcache_globals));
  115. #ifdef HAVE_XCACHE_CONSTANT
  116. zend_hash_init_ex(&xcache_globals->internal_constant_table, 1, NULL, (dtor_func_t) xc_zend_constant_dtor, 1, 0);
  117. #endif
  118. zend_hash_init_ex(&xcache_globals->internal_function_table, 1, NULL, NULL, 1, 0);
  119. zend_hash_init_ex(&xcache_globals->internal_class_table, 1, NULL, NULL, 1, 0);
  120. }
  121. /* }}} */
  122. /* {{{ PHP_GSHUTDOWN_FUNCTION(xcache) */
  123. static
  124. #ifdef PHP_GSHUTDOWN_FUNCTION
  125. PHP_GSHUTDOWN_FUNCTION(xcache)
  126. #else
  127. void xc_shutdown_globals(zend_xcache_globals* xcache_globals TSRMLS_DC)
  128. #endif
  129. {
  130. size_t i;
  131. if (xcache_globals->php_holds != NULL) {
  132. for (i = 0; i < xcache_globals->php_holds_size; i ++) {
  133. xc_vector_destroy(&xcache_globals->php_holds[i]);
  134. }
  135. free(xcache_globals->php_holds);
  136. xcache_globals->php_holds = NULL;
  137. xcache_globals->php_holds_size = 0;
  138. }
  139. if (xcache_globals->var_holds != NULL) {
  140. for (i = 0; i < xcache_globals->var_holds_size; i ++) {
  141. xc_vector_destroy(&xcache_globals->var_holds[i]);
  142. }
  143. free(xcache_globals->var_holds);
  144. xcache_globals->var_holds = NULL;
  145. xcache_globals->var_holds_size = 0;
  146. }
  147. if (xcache_globals->internal_table_copied) {
  148. #ifdef HAVE_XCACHE_CONSTANT
  149. zend_hash_destroy(&xcache_globals->internal_constant_table);
  150. #endif
  151. zend_hash_destroy(&xcache_globals->internal_function_table);
  152. zend_hash_destroy(&xcache_globals->internal_class_table);
  153. }
  154. }
  155. /* }}} */
  156. #ifdef HAVE_XCACHE_DPRINT
  157. /* {{{ proto bool xcache_dprint(mixed value)
  158. Prints internal struct of an zval (debug only) */
  159. #include "xc_processor.h"
  160. #ifdef ZEND_BEGIN_ARG_INFO_EX
  161. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_dprint, 0, 0, 1)
  162. ZEND_ARG_INFO(0, value)
  163. ZEND_END_ARG_INFO()
  164. #else
  165. static unsigned char arginfo_xcache_dprint[] = { 1, BYREF_NONE };
  166. #endif
  167. PHP_FUNCTION(xcache_dprint)
  168. {
  169. zval *value;
  170. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  171. return;
  172. }
  173. xc_dprint_zval(value, 0 TSRMLS_CC);
  174. }
  175. /* }}} */
  176. #endif
  177. #ifdef HAVE_XCACHE_ASSEMBLER
  178. /* {{{ proto string xcache_asm(string filename)
  179. */
  180. PHP_FUNCTION(xcache_asm)
  181. {
  182. }
  183. /* }}} */
  184. #endif
  185. #ifdef HAVE_XCACHE_ENCODER
  186. /* {{{ proto string xcache_encode(string filename)
  187. Encode php file into XCache opcode encoded format */
  188. PHP_FUNCTION(xcache_encode)
  189. {
  190. }
  191. /* }}} */
  192. #endif
  193. #ifdef HAVE_XCACHE_DECODER
  194. /* {{{ proto bool xcache_decode_file(string filename)
  195. Decode(load) opcode from XCache encoded format file */
  196. PHP_FUNCTION(xcache_decode_file)
  197. {
  198. }
  199. /* }}} */
  200. /* {{{ proto bool xcache_decode_string(string data)
  201. Decode(load) opcode from XCache encoded format data */
  202. PHP_FUNCTION(xcache_decode_string)
  203. {
  204. }
  205. /* }}} */
  206. #endif
  207. /* {{{ xc_call_getter */
  208. typedef const char *(xc_name_getter_t)(zend_uchar type);
  209. static void xc_call_getter(xc_name_getter_t getter, int count, INTERNAL_FUNCTION_PARAMETERS)
  210. {
  211. long spec;
  212. const char *name;
  213. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  214. return;
  215. }
  216. if (spec >= 0 && spec < count) {
  217. name = getter((zend_uchar) spec);
  218. if (name) {
  219. /* RETURN_STRING */
  220. int len = (int) strlen(name);
  221. return_value->value.str.len = len;
  222. return_value->value.str.val = estrndup(name, len);
  223. return_value->type = IS_STRING;
  224. return;
  225. }
  226. }
  227. RETURN_NULL();
  228. }
  229. /* }}} */
  230. /* {{{ proto string xcache_get_op_type(int op_type) */
  231. #ifdef ZEND_BEGIN_ARG_INFO_EX
  232. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_op_type, 0, 0, 1)
  233. ZEND_ARG_INFO(0, op_type)
  234. ZEND_END_ARG_INFO()
  235. #else
  236. static unsigned char arginfo_xcache_get_op_type[] = { 1, BYREF_NONE };
  237. #endif
  238. PHP_FUNCTION(xcache_get_op_type)
  239. {
  240. xc_call_getter(xc_get_op_type, xc_get_op_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  241. }
  242. /* }}} */
  243. /* {{{ proto string xcache_get_data_type(int type) */
  244. #ifdef ZEND_BEGIN_ARG_INFO_EX
  245. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_data_type, 0, 0, 1)
  246. ZEND_ARG_INFO(0, type)
  247. ZEND_END_ARG_INFO()
  248. #else
  249. static unsigned char arginfo_xcache_get_data_type[] = { 1, BYREF_NONE };
  250. #endif
  251. PHP_FUNCTION(xcache_get_data_type)
  252. {
  253. xc_call_getter(xc_get_data_type, xc_get_data_type_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  254. }
  255. /* }}} */
  256. /* {{{ proto string xcache_get_opcode(int opcode) */
  257. #ifdef ZEND_BEGIN_ARG_INFO_EX
  258. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_opcode, 0, 0, 1)
  259. ZEND_ARG_INFO(0, opcode)
  260. ZEND_END_ARG_INFO()
  261. #else
  262. static unsigned char arginfo_xcache_get_opcode[] = { 1, BYREF_NONE };
  263. #endif
  264. PHP_FUNCTION(xcache_get_opcode)
  265. {
  266. xc_call_getter(xc_get_opcode, xc_get_opcode_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  267. }
  268. /* }}} */
  269. /* {{{ proto string xcache_get_op_spec(int op_type) */
  270. #ifdef ZEND_BEGIN_ARG_INFO_EX
  271. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_op_spec, 0, 0, 1)
  272. ZEND_ARG_INFO(0, op_type)
  273. ZEND_END_ARG_INFO()
  274. #else
  275. static unsigned char arginfo_xcache_get_op_spec[] = { 1, BYREF_NONE };
  276. #endif
  277. PHP_FUNCTION(xcache_get_op_spec)
  278. {
  279. xc_call_getter(xc_get_op_spec, xc_get_op_spec_count(), INTERNAL_FUNCTION_PARAM_PASSTHRU);
  280. }
  281. /* }}} */
  282. /* {{{ proto string xcache_get_opcode_spec(int opcode) */
  283. #ifdef ZEND_BEGIN_ARG_INFO_EX
  284. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_opcode_spec, 0, 0, 1)
  285. ZEND_ARG_INFO(0, opcode)
  286. ZEND_END_ARG_INFO()
  287. #else
  288. static unsigned char arginfo_xcache_get_opcode_spec[] = { 1, BYREF_NONE };
  289. #endif
  290. PHP_FUNCTION(xcache_get_opcode_spec)
  291. {
  292. long spec;
  293. const xc_opcode_spec_t *opspec;
  294. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &spec) == FAILURE) {
  295. return;
  296. }
  297. if ((zend_uchar) spec <= xc_get_opcode_spec_count()) {
  298. opspec = xc_get_opcode_spec((zend_uchar) spec);
  299. if (opspec) {
  300. array_init(return_value);
  301. add_assoc_long_ex(return_value, XCACHE_STRS("ext"), opspec->ext);
  302. add_assoc_long_ex(return_value, XCACHE_STRS("op1"), opspec->op1);
  303. add_assoc_long_ex(return_value, XCACHE_STRS("op2"), opspec->op2);
  304. add_assoc_long_ex(return_value, XCACHE_STRS("res"), opspec->res);
  305. return;
  306. }
  307. }
  308. RETURN_NULL();
  309. }
  310. /* }}} */
  311. /* {{{ proto mixed xcache_get_special_value(zval value)
  312. XCache internal use only: For decompiler to get static value with type fixed */
  313. #ifdef ZEND_BEGIN_ARG_INFO_EX
  314. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_special_value, 0, 0, 1)
  315. ZEND_ARG_INFO(0, value)
  316. ZEND_END_ARG_INFO()
  317. #else
  318. static unsigned char arginfo_xcache_get_special_value[] = { 1, BYREF_NONE };
  319. #endif
  320. PHP_FUNCTION(xcache_get_special_value)
  321. {
  322. zval *value;
  323. zval value_copied;
  324. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  325. return;
  326. }
  327. value_copied = *value;
  328. value = &value_copied;
  329. switch ((Z_TYPE_P(value) & IS_CONSTANT_TYPE_MASK)) {
  330. case IS_CONSTANT:
  331. value->type = UNISW(IS_STRING, UG(unicode) ? IS_UNICODE : IS_STRING);
  332. RETURN_ZVAL(value, 1, 0);
  333. break;
  334. #ifdef IS_CONSTANT_ARRAY
  335. case IS_CONSTANT_ARRAY:
  336. value->type = IS_ARRAY;
  337. RETURN_ZVAL(value, 1, 0);
  338. break;
  339. #endif
  340. #ifdef IS_CONSTANT_AST
  341. case IS_CONSTANT_AST:
  342. RETURN_NULL();
  343. break;
  344. #endif
  345. default:
  346. if ((Z_TYPE_P(value) & ~IS_CONSTANT_TYPE_MASK)) {
  347. value->type &= IS_CONSTANT_TYPE_MASK;
  348. RETURN_ZVAL(value, 1, 0);
  349. }
  350. else {
  351. RETURN_NULL();
  352. }
  353. }
  354. }
  355. /* }}} */
  356. /* {{{ proto int xcache_get_type(zval value)
  357. XCache internal use only for disassembler to get variable type in engine level */
  358. #ifdef ZEND_BEGIN_ARG_INFO_EX
  359. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_type, 0, 0, 1)
  360. ZEND_ARG_INFO(0, value)
  361. ZEND_END_ARG_INFO()
  362. #else
  363. static unsigned char arginfo_xcache_get_type[] = { 1, BYREF_NONE };
  364. #endif
  365. PHP_FUNCTION(xcache_get_type)
  366. {
  367. zval *value;
  368. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &value) == FAILURE) {
  369. return;
  370. }
  371. RETURN_LONG(Z_TYPE_P(value));
  372. }
  373. /* }}} */
  374. /* {{{ proto string xcache_coredump() */
  375. #ifdef ZEND_BEGIN_ARG_INFO_EX
  376. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_coredump, 0, 0, 0)
  377. ZEND_END_ARG_INFO()
  378. #else
  379. static unsigned char arginfo_xcache_coredump[] = { 1, BYREF_NONE };
  380. #endif
  381. PHP_FUNCTION(xcache_coredump)
  382. {
  383. if (xc_test) {
  384. char *null_ptr = NULL;
  385. *null_ptr = 0;
  386. raise(SIGSEGV);
  387. }
  388. else {
  389. php_error_docref(NULL TSRMLS_CC, E_WARNING, "xcache.test must be enabled to test xcache_coredump()");
  390. }
  391. }
  392. /* }}} */
  393. /* {{{ proto string xcache_is_autoglobal(string name) */
  394. #ifdef ZEND_BEGIN_ARG_INFO_EX
  395. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_is_autoglobal, 0, 0, 1)
  396. ZEND_ARG_INFO(0, name)
  397. ZEND_END_ARG_INFO()
  398. #else
  399. static unsigned char arginfo_xcache_is_autoglobal[] = { 1, BYREF_NONE };
  400. #endif
  401. PHP_FUNCTION(xcache_is_autoglobal)
  402. {
  403. zval *name;
  404. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &name) == FAILURE) {
  405. return;
  406. }
  407. #ifdef IS_UNICODE
  408. convert_to_unicode(name);
  409. #else
  410. convert_to_string(name);
  411. #endif
  412. RETURN_BOOL(zend_u_hash_exists(CG(auto_globals), UG(unicode), Z_STRVAL_P(name), Z_STRLEN_P(name) + 1));
  413. }
  414. /* }}} */
  415. /* {{{ proto int xcache_get_refcount(mixed &variable)
  416. XCache internal uses only: Get reference count of referenced variable */
  417. #ifdef ZEND_BEGIN_ARG_INFO_EX
  418. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_refcount, 0, 0, 1)
  419. ZEND_ARG_INFO(1, variable)
  420. ZEND_END_ARG_INFO()
  421. #else
  422. static unsigned char arginfo_xcache_get_refcount[] = { 1, BYREF_FORCE };
  423. #endif
  424. PHP_FUNCTION(xcache_get_refcount)
  425. {
  426. zval *variable;
  427. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  428. RETURN_NULL();
  429. }
  430. RETURN_LONG(Z_REFCOUNT(*variable) - 1);
  431. }
  432. /* }}} */
  433. /* {{{ proto int xcache_get_cowcount(mixed value)
  434. XCache internal uses only: Get reference count of copy-on-write variable or data */
  435. #ifdef ZEND_BEGIN_ARG_INFO_EX
  436. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_cowcount, 0, 0, 1)
  437. ZEND_ARG_INFO(0, variable)
  438. ZEND_END_ARG_INFO()
  439. #else
  440. static unsigned char arginfo_xcache_get_cowcount[] = { 1, BYREF_NONE };
  441. #endif
  442. PHP_FUNCTION(xcache_get_cowcount)
  443. {
  444. zval *variable;
  445. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  446. RETURN_NULL();
  447. }
  448. RETURN_LONG(Z_REFCOUNT(*variable) - 1);
  449. }
  450. /* }}} */
  451. /* {{{ proto bool xcache_get_isref(mixed variable)
  452. XCache internal uses only: Check if variable data is marked referenced */
  453. #ifdef ZEND_BEGIN_ARG_INFO_EX
  454. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_get_isref, 0, 0, 1)
  455. ZEND_ARG_INFO(1, variable)
  456. ZEND_END_ARG_INFO()
  457. #else
  458. static unsigned char arginfo_xcache_get_isref[] = { 1, BYREF_FORCE };
  459. #endif
  460. PHP_FUNCTION(xcache_get_isref)
  461. {
  462. zval *variable;
  463. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &variable) == FAILURE) {
  464. RETURN_NULL();
  465. }
  466. RETURN_BOOL(Z_ISREF(*variable) && Z_REFCOUNT(*variable) >= 3);
  467. }
  468. /* }}} */
  469. static zend_function_entry xcache_functions[] = /* {{{ */
  470. {
  471. #ifdef HAVE_XCACHE_DPRINT
  472. PHP_FE(xcache_dprint, arginfo_xcache_dprint)
  473. #endif
  474. #ifdef HAVE_XCACHE_ASSEMBLER
  475. PHP_FE(xcache_asm, NULL)
  476. #endif
  477. #ifdef HAVE_XCACHE_ENCODER
  478. PHP_FE(xcache_encode, NULL)
  479. #endif
  480. #ifdef HAVE_XCACHE_DECODER
  481. PHP_FE(xcache_decode_file, NULL)
  482. PHP_FE(xcache_decode_string, NULL)
  483. #endif
  484. PHP_FE(xcache_get_special_value, arginfo_xcache_get_special_value)
  485. PHP_FE(xcache_get_type, arginfo_xcache_get_type)
  486. PHP_FE(xcache_get_op_type, arginfo_xcache_get_op_type)
  487. PHP_FE(xcache_get_data_type, arginfo_xcache_get_data_type)
  488. PHP_FE(xcache_get_opcode, arginfo_xcache_get_opcode)
  489. PHP_FE(xcache_get_op_spec, arginfo_xcache_get_op_spec)
  490. PHP_FE(xcache_get_opcode_spec, arginfo_xcache_get_opcode_spec)
  491. PHP_FE(xcache_coredump, arginfo_xcache_coredump)
  492. PHP_FE(xcache_is_autoglobal, arginfo_xcache_is_autoglobal)
  493. PHP_FE(xcache_get_refcount, arginfo_xcache_get_refcount)
  494. PHP_FE(xcache_get_cowcount, arginfo_xcache_get_cowcount)
  495. PHP_FE(xcache_get_isref, arginfo_xcache_get_isref)
  496. PHP_FE_END
  497. };
  498. /* }}} */
  499. #ifdef ZEND_WIN32
  500. #include "dbghelp.h"
  501. typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
  502. CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
  503. CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
  504. CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
  505. );
  506. static PTOP_LEVEL_EXCEPTION_FILTER oldFilter = NULL;
  507. static HMODULE dbghelpModule = NULL;
  508. static char crash_dumpPath[_MAX_PATH] = { 0 };
  509. static MINIDUMPWRITEDUMP dbghelp_MiniDumpWriteDump = NULL;
  510. static LONG WINAPI miniDumperFilter(struct _EXCEPTION_POINTERS *pExceptionInfo) /* {{{ */
  511. {
  512. HANDLE fileHandle;
  513. LONG ret = EXCEPTION_CONTINUE_SEARCH;
  514. SetUnhandledExceptionFilter(oldFilter);
  515. /* create the file */
  516. fileHandle = CreateFile(crash_dumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
  517. if (fileHandle != INVALID_HANDLE_VALUE) {
  518. MINIDUMP_EXCEPTION_INFORMATION exceptionInformation;
  519. MINIDUMP_TYPE type = xc_coredump_type ? xc_coredump_type : (MiniDumpNormal|MiniDumpWithDataSegs|MiniDumpWithIndirectlyReferencedMemory);
  520. BOOL ok;
  521. exceptionInformation.ThreadId = GetCurrentThreadId();
  522. exceptionInformation.ExceptionPointers = pExceptionInfo;
  523. exceptionInformation.ClientPointers = FALSE;
  524. /* write the dump */
  525. ok = dbghelp_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), fileHandle, type, &exceptionInformation, NULL, NULL);
  526. CloseHandle(fileHandle);
  527. if (ok) {
  528. zend_error(E_ERROR, "Saved dump file to '%s'", crash_dumpPath);
  529. ret = EXCEPTION_EXECUTE_HANDLER;
  530. }
  531. else {
  532. zend_error(E_ERROR, "Failed to save dump file to '%s' (error %d)", crash_dumpPath, GetLastError());
  533. }
  534. }
  535. else {
  536. zend_error(E_ERROR, "Failed to create dump file '%s' (error %d)", crash_dumpPath, GetLastError());
  537. }
  538. if (xc_disable_on_crash) {
  539. xc_disable_on_crash = 0;
  540. xc_cacher_disable();
  541. }
  542. return ret;
  543. }
  544. /* }}} */
  545. static void xcache_restore_crash_handler() /* {{{ */
  546. {
  547. if (oldFilter) {
  548. SetUnhandledExceptionFilter(oldFilter);
  549. oldFilter = NULL;
  550. }
  551. }
  552. /* }}} */
  553. static void xcache_init_crash_handler() /* {{{ */
  554. {
  555. /* firstly see if dbghelp.dll is around and has the function we need
  556. look next to the EXE first, as the one in System32 might be old
  557. (e.g. Windows 2000) */
  558. char dbghelpPath[_MAX_PATH];
  559. if (GetModuleFileName(NULL, dbghelpPath, _MAX_PATH)) {
  560. char *slash = strchr(dbghelpPath, '\\');
  561. if (slash) {
  562. strcpy(slash + 1, "DBGHELP.DLL");
  563. dbghelpModule = LoadLibrary(dbghelpPath);
  564. }
  565. }
  566. if (!dbghelpModule) {
  567. /* load any version we can */
  568. dbghelpModule = LoadLibrary("DBGHELP.DLL");
  569. if (!dbghelpModule) {
  570. zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL not found");
  571. return;
  572. }
  573. }
  574. dbghelp_MiniDumpWriteDump = (MINIDUMPWRITEDUMP)GetProcAddress(dbghelpModule, "MiniDumpWriteDump");
  575. if (!dbghelp_MiniDumpWriteDump) {
  576. zend_error(E_ERROR, "Unable to enable crash dump saver: %s", "DBGHELP.DLL too old. Get updated dll and put it aside of php_xcache.dll");
  577. return;
  578. }
  579. #ifdef XCACHE_VERSION_REVISION
  580. #define REVISION "r" XCACHE_VERSION_REVISION
  581. #else
  582. #define REVISION ""
  583. #endif
  584. sprintf(crash_dumpPath, "%s\\php-%s-xcache-%s%s-%lu-%lu.dmp", xc_coredump_dir, zend_get_module_version("standard"), XCACHE_VERSION, REVISION, (unsigned long) time(NULL), (unsigned long) GetCurrentProcessId());
  585. #undef REVISION
  586. oldFilter = SetUnhandledExceptionFilter(&miniDumperFilter);
  587. }
  588. /* }}} */
  589. #else
  590. /* old signal handlers {{{ */
  591. typedef void (*xc_sighandler_t)(int);
  592. #define FOREACH_SIG(sig) static xc_sighandler_t old_##sig##_handler = NULL
  593. #include "util/xc_foreachcoresig.h"
  594. #undef FOREACH_SIG
  595. /* }}} */
  596. static void xcache_signal_handler(int sig);
  597. static void xcache_restore_crash_handler() /* {{{ */
  598. {
  599. #define FOREACH_SIG(sig) do { \
  600. if (old_##sig##_handler != xcache_signal_handler) { \
  601. signal(sig, old_##sig##_handler); \
  602. } \
  603. else { \
  604. signal(sig, SIG_DFL); \
  605. } \
  606. } while (0)
  607. #include "util/xc_foreachcoresig.h"
  608. #undef FOREACH_SIG
  609. }
  610. /* }}} */
  611. static void xcache_init_crash_handler() /* {{{ */
  612. {
  613. #define FOREACH_SIG(sig) \
  614. old_##sig##_handler = signal(sig, xcache_signal_handler)
  615. #include "util/xc_foreachcoresig.h"
  616. #undef FOREACH_SIG
  617. }
  618. /* }}} */
  619. static void xcache_signal_handler(int sig) /* {{{ */
  620. {
  621. xcache_restore_crash_handler();
  622. if (xc_coredump_dir && xc_coredump_dir[0]) {
  623. if (chdir(xc_coredump_dir) != 0) {
  624. /* error, but nothing can do about it
  625. * and should'nt print anything which might SEGV again */
  626. }
  627. }
  628. if (xc_disable_on_crash) {
  629. xc_disable_on_crash = 0;
  630. xc_cacher_disable();
  631. }
  632. if (0) {
  633. }
  634. #define FOREACH_SIG(forsig) \
  635. else if (sig == forsig) \
  636. write(2, XCACHE_STRL("Program received signal " #forsig ", Segmentation fault\n"))
  637. #include "util/xc_foreachcoresig.h"
  638. #undef FOREACH_SIG
  639. write(2, XCACHE_STRL("If core dump is enabled it can be found at "));
  640. write(2, xc_coredump_dir, strlen(xc_coredump_dir));
  641. write(2, XCACHE_STRL("\n"));
  642. raise(sig);
  643. }
  644. /* }}} */
  645. #endif
  646. static zend_bool xc_is_xcache(const char *name) /* {{{ */
  647. {
  648. return strcmp(name, XCACHE_NAME) == 0 || strncmp(name, XCACHE_NAME " ", sizeof(XCACHE_NAME " ") - 1) == 0;
  649. }
  650. /* }}} */
  651. /* {{{ incompatible zend extensions handling */
  652. typedef struct {
  653. const char *name;
  654. startup_func_t old_startup;
  655. } xc_incompatible_zend_extension_info_t;
  656. static xc_incompatible_zend_extension_info_t xc_incompatible_zend_extensions[] = {
  657. { "Zend Extension Manager", NULL },
  658. { "Zend Optimizer", NULL },
  659. { "the ionCube PHP Loader", NULL }
  660. };
  661. static xc_incompatible_zend_extension_info_t *xc_get_incompatible_zend_extension_info(const char *name)
  662. {
  663. size_t i;
  664. for (i = 0; i < sizeof(xc_incompatible_zend_extensions) / sizeof(xc_incompatible_zend_extensions[0]); ++i) {
  665. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = &xc_incompatible_zend_extensions[i];
  666. if (strcmp(incompatible_zend_extension_info->name, name) == 0) {
  667. return incompatible_zend_extension_info;
  668. }
  669. }
  670. return NULL;
  671. }
  672. /* }}} */
  673. static void xc_zend_llist_add_element(zend_llist *list, zend_llist_element *element) /* {{{ */
  674. {
  675. if (!zend_extensions.head) {
  676. zend_extensions.head = zend_extensions.tail = element;
  677. }
  678. else {
  679. zend_extensions.tail->next = element;
  680. element->prev = zend_extensions.tail;
  681. zend_extensions.tail = element;
  682. }
  683. }
  684. /* }}} */
  685. static int xc_incompatible_zend_extension_startup_hook(zend_extension *extension) /* {{{ */
  686. {
  687. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = xc_get_incompatible_zend_extension_info(extension->name);
  688. int status;
  689. zend_bool catched = 0;
  690. zend_llist saved_zend_extensions_container; /* without elements */
  691. zend_llist_element **saved_zend_extensions_elments;
  692. size_t new_zend_extensions_elments_count;
  693. zend_llist_element **new_zend_extensions_elments;
  694. zend_extension *ext;
  695. size_t i;
  696. zend_llist_element *element;
  697. TSRMLS_FETCH();
  698. /* restore startup hack */
  699. extension->startup = incompatible_zend_extension_info->old_startup;
  700. incompatible_zend_extension_info->old_startup = NULL;
  701. assert(extension->startup);
  702. /* save extensions list */
  703. saved_zend_extensions_container = zend_extensions;
  704. saved_zend_extensions_elments = malloc(sizeof(zend_llist_element *) * saved_zend_extensions_container.count);
  705. for (i = 0, element = saved_zend_extensions_container.head; element; ++i, element = element->next) {
  706. saved_zend_extensions_elments[i] = element;
  707. }
  708. /* hide all XCache extensions from it */
  709. zend_extensions.head = NULL;
  710. zend_extensions.tail = NULL;
  711. zend_extensions.count = 0;
  712. for (i = 0; i < saved_zend_extensions_container.count; ++i) {
  713. element = saved_zend_extensions_elments[i];
  714. element->next = element->prev = NULL;
  715. ext = (zend_extension *) element->data;
  716. if (!xc_is_xcache(ext->name)) {
  717. xc_zend_llist_add_element(&zend_extensions, element);
  718. ++zend_extensions.count;
  719. }
  720. }
  721. assert(extension->startup != xc_incompatible_zend_extension_startup_hook);
  722. zend_try {
  723. status = extension->startup(extension);
  724. } zend_catch {
  725. catched = 1;
  726. } zend_end_try();
  727. /* save newly added extensions added by this extension*/
  728. new_zend_extensions_elments_count = zend_extensions.count - 1;
  729. new_zend_extensions_elments = NULL;
  730. if (new_zend_extensions_elments_count) {
  731. new_zend_extensions_elments = malloc(sizeof(zend_llist_element *) * new_zend_extensions_elments_count);
  732. element = zend_extensions.head;
  733. for (i = 0, element = element->next; element; ++i, element = element->next) {
  734. new_zend_extensions_elments[i] = element;
  735. }
  736. }
  737. /* restore original extension list*/
  738. zend_extensions = saved_zend_extensions_container;
  739. zend_extensions.head = NULL;
  740. zend_extensions.tail = NULL;
  741. zend_extensions.count = 0;
  742. for (i = 0; i < saved_zend_extensions_container.count; ++i) {
  743. element = saved_zend_extensions_elments[i];
  744. element->next = element->prev = NULL;
  745. xc_zend_llist_add_element(&zend_extensions, element);
  746. ++zend_extensions.count;
  747. ext = (zend_extension *) element->data;
  748. if (ext == extension && new_zend_extensions_elments_count) {
  749. /* add new created extension */
  750. size_t j;
  751. for (j = 0; j < new_zend_extensions_elments_count; ++j) {
  752. element = new_zend_extensions_elments[j];
  753. element->next = element->prev = NULL;
  754. xc_zend_llist_add_element(&zend_extensions, element);
  755. ++zend_extensions.count;
  756. }
  757. }
  758. }
  759. free(saved_zend_extensions_elments);
  760. if (new_zend_extensions_elments) {
  761. free(new_zend_extensions_elments);
  762. }
  763. if (catched) {
  764. zend_bailout();
  765. }
  766. return status;
  767. }
  768. /* }}} */
  769. static int xc_zend_startup(zend_extension *extension) /* {{{ */
  770. {
  771. zend_llist_position lpos;
  772. zend_extension *ext;
  773. ext = (zend_extension *) zend_extensions.head->data;
  774. if (strcmp(ext->name, XCACHE_NAME) != 0) {
  775. zend_error(E_WARNING, "XCache failed to load itself to before zend_extension=\"%s\". compatibility downgraded", ext->name);
  776. }
  777. for (ext = (zend_extension *) zend_llist_get_first_ex(&zend_extensions, &lpos);
  778. ext;
  779. ext = (zend_extension *) zend_llist_get_next_ex(&zend_extensions, &lpos)) {
  780. xc_incompatible_zend_extension_info_t *incompatible_zend_extension_info = xc_get_incompatible_zend_extension_info(ext->name);
  781. if (incompatible_zend_extension_info) {
  782. assert(!incompatible_zend_extension_info->old_startup);
  783. incompatible_zend_extension_info->old_startup = ext->startup;
  784. ext->startup = xc_incompatible_zend_extension_startup_hook;
  785. }
  786. }
  787. return SUCCESS;
  788. }
  789. /* }}} */
  790. static void xc_zend_shutdown(zend_extension *extension) /* {{{ */
  791. {
  792. }
  793. /* }}} */
  794. /* {{{ zend extension definition structure */
  795. static zend_extension xc_zend_extension_entry = {
  796. XCACHE_NAME,
  797. XCACHE_VERSION,
  798. XCACHE_AUTHOR,
  799. XCACHE_URL,
  800. XCACHE_COPYRIGHT,
  801. xc_zend_startup,
  802. xc_zend_shutdown,
  803. NULL, /* activate_func_t */
  804. NULL, /* deactivate_func_t */
  805. NULL, /* message_handler_func_t */
  806. NULL, /* op_array_handler_func_t */
  807. NULL, /* statement_handler_func_t */
  808. NULL, /* fcall_begin_handler_func_t */
  809. NULL, /* fcall_end_handler_func_t */
  810. NULL, /* op_array_ctor_func_t */
  811. NULL, /* op_array_dtor_func_t */
  812. STANDARD_ZEND_EXTENSION_PROPERTIES
  813. };
  814. /* }}} */
  815. /* {{{ PHP_INI */
  816. PHP_INI_BEGIN()
  817. PHP_INI_ENTRY1 ("xcache.coredump_directory", "", PHP_INI_SYSTEM, xcache_OnUpdateString, &xc_coredump_dir)
  818. #ifdef ZEND_WIN32
  819. PHP_INI_ENTRY1 ("xcache.coredump_type", "0", PHP_INI_SYSTEM, xcache_OnUpdateULong, &xc_coredump_type)
  820. #endif
  821. PHP_INI_ENTRY1_EX ("xcache.disable_on_crash", "0", PHP_INI_SYSTEM, xcache_OnUpdateBool, &xc_disable_on_crash, zend_ini_boolean_displayer_cb)
  822. PHP_INI_ENTRY1_EX ("xcache.test", "0", PHP_INI_SYSTEM, xcache_OnUpdateBool, &xc_test, zend_ini_boolean_displayer_cb)
  823. STD_PHP_INI_BOOLEAN("xcache.experimental", "0", PHP_INI_ALL, OnUpdateBool, experimental, zend_xcache_globals, xcache_globals)
  824. PHP_INI_END()
  825. /* }}} */
  826. static PHP_MINFO_FUNCTION(xcache) /* {{{ */
  827. {
  828. php_info_print_table_start();
  829. php_info_print_table_row(2, "XCache Version", XCACHE_VERSION);
  830. #ifdef XCACHE_VERSION_REVISION
  831. php_info_print_table_row(2, "Revision", "r" XCACHE_VERSION_REVISION);
  832. #endif
  833. php_info_print_table_row(2, "Modules Built", XCACHE_MODULES);
  834. php_info_print_table_end();
  835. DISPLAY_INI_ENTRIES();
  836. }
  837. /* }}} */
  838. static PHP_MINIT_FUNCTION(xcache) /* {{{ */
  839. {
  840. #ifndef PHP_GINIT
  841. ZEND_INIT_MODULE_GLOBALS(xcache, xc_init_globals, xc_shutdown_globals);
  842. #endif
  843. REGISTER_INI_ENTRIES();
  844. if (xc_coredump_dir && xc_coredump_dir[0]) {
  845. xcache_init_crash_handler();
  846. }
  847. if (strcmp(sapi_module.name, "cli") == 0) {
  848. char *env;
  849. if ((env = getenv("XCACHE_TEST")) != NULL) {
  850. xc_test = (zend_bool) atoi(env);
  851. }
  852. }
  853. xc_init_constant(module_number TSRMLS_CC);
  854. xc_shm_init_modules();
  855. /* must be the first */
  856. xcache_zend_extension_add(&xc_zend_extension_entry, 1);
  857. old_compile_file = zend_compile_file;
  858. zend_compile_file = xc_check_initial_compile_file;
  859. #ifdef HAVE_XCACHE_OPTIMIZER
  860. xc_optimizer_startup_module();
  861. #endif
  862. #ifdef HAVE_XCACHE_CACHER
  863. xc_cacher_startup_module();
  864. #endif
  865. #ifdef HAVE_XCACHE_COVERAGER
  866. xc_coverager_startup_module();
  867. #endif
  868. #ifdef HAVE_XCACHE_DISASSEMBLER
  869. xc_disassembler_startup_module();
  870. #endif
  871. return SUCCESS;
  872. }
  873. /* }}} */
  874. static PHP_MSHUTDOWN_FUNCTION(xcache) /* {{{ */
  875. {
  876. if (old_compile_file && zend_compile_file == xc_check_initial_compile_file) {
  877. zend_compile_file = old_compile_file;
  878. old_compile_file = NULL;
  879. }
  880. if (xc_coredump_dir && xc_coredump_dir[0]) {
  881. xcache_restore_crash_handler();
  882. }
  883. if (xc_coredump_dir) {
  884. pefree(xc_coredump_dir, 1);
  885. xc_coredump_dir = NULL;
  886. }
  887. #ifndef PHP_GINIT
  888. # ifdef ZTS
  889. ts_free_id(xcache_globals_id);
  890. # else
  891. xc_shutdown_globals(&xcache_globals TSRMLS_CC);
  892. # endif
  893. #endif
  894. UNREGISTER_INI_ENTRIES();
  895. xcache_zend_extension_remove(&xc_zend_extension_entry);
  896. #ifndef ZEND_ENGINE_2
  897. /* XCache main module is registered last of all XCache modules in PHP_4
  898. * move handle to first XCache module to avoid early unload of this dll(so)
  899. */
  900. {
  901. zend_module_entry *handle_holder = NULL, *first = NULL;
  902. Bucket *p;
  903. for (p = module_registry.pListHead; p; p = p->pListNext) {
  904. zend_module_entry *module = p->pData;
  905. if (xc_is_xcache(module->name)) {
  906. if (!first) {
  907. first = module;
  908. }
  909. if (module->handle) {
  910. handle_holder = module;
  911. break;
  912. }
  913. }
  914. }
  915. if (first && handle_holder && handle_holder != first) {
  916. first->handle = handle_holder->handle;
  917. handle_holder->handle = NULL;
  918. }
  919. }
  920. #endif
  921. return SUCCESS;
  922. }
  923. /* }}} */
  924. /* {{{ module dependencies */
  925. #ifdef STANDARD_MODULE_HEADER_EX
  926. static zend_module_dep xcache_module_deps[] = {
  927. ZEND_MOD_REQUIRED("standard")
  928. ZEND_MOD_CONFLICTS("apc")
  929. ZEND_MOD_CONFLICTS("eAccelerator")
  930. ZEND_MOD_CONFLICTS("Turck MMCache")
  931. ZEND_MOD_END
  932. };
  933. #endif
  934. /* }}} */
  935. /* {{{ module definition structure */
  936. zend_module_entry xcache_module_entry = {
  937. #ifdef STANDARD_MODULE_HEADER_EX
  938. STANDARD_MODULE_HEADER_EX,
  939. NULL,
  940. xcache_module_deps,
  941. #else
  942. STANDARD_MODULE_HEADER,
  943. #endif
  944. XCACHE_NAME,
  945. xcache_functions,
  946. PHP_MINIT(xcache),
  947. PHP_MSHUTDOWN(xcache),
  948. NULL, /* RINIT */
  949. NULL, /* RSHUTDOWN */
  950. PHP_MINFO(xcache),
  951. XCACHE_VERSION,
  952. #ifdef PHP_GINIT
  953. PHP_MODULE_GLOBALS(xcache),
  954. PHP_GINIT(xcache),
  955. PHP_GSHUTDOWN(xcache),
  956. #endif
  957. #ifdef ZEND_ENGINE_2
  958. NULL /* ZEND_MODULE_POST_ZEND_DEACTIVATE_N */,
  959. #else
  960. NULL,
  961. NULL,
  962. #endif
  963. STANDARD_MODULE_PROPERTIES_EX
  964. };
  965. #ifdef COMPILE_DL_XCACHE
  966. ZEND_GET_MODULE(xcache)
  967. #endif
  968. /* }}} */