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.

745 lines
18 KiB

  1. #include "xc_coverager.h"
  2. #include <stdio.h>
  3. #include "xcache.h"
  4. #include "ext/standard/flock_compat.h"
  5. #ifdef HAVE_SYS_FILE_H
  6. # include <sys/file.h>
  7. #endif
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #include <fcntl.h>
  11. #include "xcache/xc_extension.h"
  12. #include "xcache/xc_ini.h"
  13. #include "xcache/xc_utils.h"
  14. #include "util/xc_vector.h"
  15. #include "util/xc_trace.h"
  16. #include "xcache_globals.h"
  17. #include "ext/standard/info.h"
  18. #include "zend_compile.h"
  19. typedef HashTable *coverager_t;
  20. #define PCOV_HEADER_MAGIC 0x564f4350
  21. static char *xc_coveragedump_dir = NULL;
  22. static zend_compile_file_t *old_compile_file = NULL;
  23. /* dumper */
  24. static void xc_destroy_coverage(void *pDest) /* {{{ */
  25. {
  26. coverager_t cov = *(coverager_t*) pDest;
  27. TRACE("destroy %p", (void *) cov);
  28. zend_hash_destroy(cov);
  29. efree(cov);
  30. }
  31. /* }}} */
  32. static void xcache_mkdirs_ex(char *root, long rootlen, char *path, long pathlen TSRMLS_DC) /* {{{ */
  33. {
  34. char *fullpath;
  35. struct stat st;
  36. ALLOCA_FLAG(use_heap)
  37. TRACE("mkdirs %s %ld %s %ld", root, rootlen, path, pathlen);
  38. fullpath = xc_do_alloca(rootlen + pathlen + 1, use_heap);
  39. memcpy(fullpath, root, rootlen);
  40. memcpy(fullpath + rootlen, path, pathlen);
  41. fullpath[rootlen + pathlen] = '\0';
  42. if (stat(fullpath, &st) != 0) {
  43. char *chr;
  44. chr = strrchr(path, PHP_DIR_SEPARATOR);
  45. if (chr && chr != path) {
  46. *chr = '\0';
  47. xcache_mkdirs_ex(root, rootlen, path, chr - path TSRMLS_CC);
  48. *chr = PHP_DIR_SEPARATOR;
  49. }
  50. TRACE("mkdir %s", fullpath);
  51. #if PHP_MAJOR_VERSION > 5
  52. php_stream_mkdir(fullpath, 0700, REPORT_ERRORS, NULL);
  53. #else
  54. mkdir(fullpath, 0700);
  55. #endif
  56. }
  57. xc_free_alloca(fullpath, use_heap);
  58. }
  59. /* }}} */
  60. static void xc_coverager_save_cov(char *srcfile, char *outfilename, coverager_t cov TSRMLS_DC) /* {{{ */
  61. {
  62. long *buf = NULL, *p;
  63. long covlines, *phits;
  64. int fd = -1;
  65. size_t size;
  66. int newfile;
  67. struct stat srcstat, outstat;
  68. HashPosition pos;
  69. char *contents = NULL;
  70. long len;
  71. if (stat(srcfile, &srcstat) != 0) {
  72. return;
  73. }
  74. newfile = 0;
  75. if (stat(outfilename, &outstat) != 0) {
  76. newfile = 1;
  77. }
  78. else {
  79. if (srcstat.st_mtime > outstat.st_mtime) {
  80. newfile = 1;
  81. }
  82. }
  83. fd = open(outfilename, O_RDWR | O_CREAT, 0600);
  84. if (fd < 0) {
  85. char *chr;
  86. chr = strrchr(srcfile, PHP_DIR_SEPARATOR);
  87. if (chr) {
  88. *chr = '\0';
  89. xcache_mkdirs_ex(xc_coveragedump_dir, strlen(xc_coveragedump_dir), srcfile, chr - srcfile TSRMLS_CC);
  90. *chr = PHP_DIR_SEPARATOR;
  91. }
  92. fd = open(outfilename, O_RDWR | O_CREAT, 0600);
  93. if (fd < 0) {
  94. goto bailout;
  95. }
  96. }
  97. if (flock(fd, LOCK_EX) != SUCCESS) {
  98. goto bailout;
  99. }
  100. if (newfile) {
  101. TRACE("%s", "new file");
  102. }
  103. else if (outstat.st_size) {
  104. len = outstat.st_size;
  105. contents = emalloc(len);
  106. if (read(fd, (void *) contents, len) != len) {
  107. goto bailout;
  108. }
  109. TRACE("oldsize %d", (int) len);
  110. do {
  111. p = (long *) contents;
  112. len -= sizeof(long);
  113. if (len < 0) {
  114. break;
  115. }
  116. if (*p++ != PCOV_HEADER_MAGIC) {
  117. TRACE("wrong magic in file %s", outfilename);
  118. break;
  119. }
  120. p += 2; /* skip covliens */
  121. len -= sizeof(long) * 2;
  122. if (len < 0) {
  123. break;
  124. }
  125. for (; len >= (int) sizeof(long) * 2; len -= sizeof(long) * 2, p += 2) {
  126. if (zend_hash_index_find(cov, p[0], (void**)&phits) == SUCCESS) {
  127. if (p[1] == -1) {
  128. /* OPTIMIZE: already marked */
  129. continue;
  130. }
  131. if (*phits != -1) {
  132. p[1] += *phits;
  133. }
  134. }
  135. zend_hash_index_update(cov, p[0], &p[1], sizeof(p[1]), NULL);
  136. }
  137. } while (0);
  138. efree(contents);
  139. contents = NULL;
  140. }
  141. /* serialize */
  142. size = (zend_hash_num_elements(cov) + 1) * sizeof(long) * 2 + sizeof(long);
  143. p = buf = emalloc(size);
  144. *p++ = PCOV_HEADER_MAGIC;
  145. p += 2; /* for covlines */
  146. covlines = 0;
  147. zend_hash_internal_pointer_reset_ex(cov, &pos);
  148. while (zend_hash_get_current_data_ex(cov, (void**)&phits, &pos) == SUCCESS) {
  149. *p++ = pos->h;
  150. *p++ = *phits;
  151. if (*phits > 0) {
  152. covlines ++;
  153. }
  154. zend_hash_move_forward_ex(cov, &pos);
  155. }
  156. p = buf + 1;
  157. p[0] = 0;
  158. p[1] = covlines;
  159. if (ftruncate(fd, 0) != 0) {
  160. goto bailout;
  161. }
  162. lseek(fd, 0, SEEK_SET);
  163. if (write(fd, (char *) buf, size) != (ssize_t) size) {
  164. goto bailout;
  165. }
  166. bailout:
  167. if (contents) efree(contents);
  168. if (fd >= 0) close(fd);
  169. if (buf) efree(buf);
  170. }
  171. /* }}} */
  172. static void xc_coverager_initenv(TSRMLS_D) /* {{{ */
  173. {
  174. if (!XG(coverages)) {
  175. XG(coverages) = emalloc(sizeof(HashTable));
  176. zend_hash_init(XG(coverages), 0, NULL, xc_destroy_coverage, 0);
  177. }
  178. }
  179. /* }}} */
  180. static void xc_coverager_clean(TSRMLS_D) /* {{{ */
  181. {
  182. if (XG(coverages)) {
  183. HashPosition pos;
  184. coverager_t *pcov;
  185. zend_hash_internal_pointer_reset_ex(XG(coverages), &pos);
  186. while (zend_hash_get_current_data_ex(XG(coverages), (void **) &pcov, &pos) == SUCCESS) {
  187. long *phits;
  188. coverager_t cov;
  189. HashPosition pos2;
  190. cov = *pcov;
  191. zend_hash_internal_pointer_reset_ex(cov, &pos2);
  192. while (zend_hash_get_current_data_ex(cov, (void**)&phits, &pos2) == SUCCESS) {
  193. long hits = *phits;
  194. if (hits != -1) {
  195. hits = -1;
  196. zend_hash_index_update(cov, pos2->h, &hits, sizeof(hits), NULL);
  197. }
  198. zend_hash_move_forward_ex(cov, &pos2);
  199. }
  200. zend_hash_move_forward_ex(XG(coverages), &pos);
  201. }
  202. }
  203. }
  204. /* }}} */
  205. static void xc_coverager_cleanup(TSRMLS_D) /* {{{ */
  206. {
  207. if (XG(coverages)) {
  208. zend_hash_destroy(XG(coverages));
  209. efree(XG(coverages));
  210. XG(coverages) = NULL;
  211. }
  212. }
  213. /* }}} */
  214. static void xc_coverager_start(TSRMLS_D) /* {{{ */
  215. {
  216. XG(coverager_started) = 1;
  217. }
  218. /* }}} */
  219. static void xc_coverager_stop(TSRMLS_D) /* {{{ */
  220. {
  221. XG(coverager_started) = 0;
  222. }
  223. /* }}} */
  224. static PHP_RINIT_FUNCTION(xcache_coverager) /* {{{ */
  225. {
  226. if (XG(coverager)) {
  227. if (XG(coverager_autostart)) {
  228. xc_coverager_start(TSRMLS_C);
  229. }
  230. #ifdef ZEND_COMPILE_EXTENDED_INFO
  231. CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
  232. #else
  233. CG(extended_info) = 1;
  234. #endif
  235. }
  236. else {
  237. XG(coverager_started) = 0;
  238. }
  239. return SUCCESS;
  240. }
  241. /* }}} */
  242. static void xc_coverager_autodump(TSRMLS_D) /* {{{ */
  243. {
  244. coverager_t *pcov;
  245. zstr s;
  246. char *outfilename;
  247. size_t dumpdir_len, outfilelen, alloc_len = 0;
  248. uint size;
  249. HashPosition pos;
  250. if (XG(coverages) && xc_coveragedump_dir) {
  251. dumpdir_len = strlen(xc_coveragedump_dir);
  252. alloc_len = dumpdir_len + 1 + 128;
  253. outfilename = emalloc(alloc_len);
  254. strcpy(outfilename, xc_coveragedump_dir);
  255. zend_hash_internal_pointer_reset_ex(XG(coverages), &pos);
  256. while (zend_hash_get_current_data_ex(XG(coverages), (void **) &pcov, &pos) == SUCCESS) {
  257. zend_hash_get_current_key_ex(XG(coverages), &s, &size, NULL, 0, &pos);
  258. outfilelen = dumpdir_len + size + 5;
  259. if (alloc_len < outfilelen) {
  260. alloc_len = outfilelen + 128;
  261. outfilename = erealloc(outfilename, alloc_len);
  262. }
  263. strcpy(outfilename + dumpdir_len, ZSTR_S(s));
  264. strcpy(outfilename + dumpdir_len + size - 1, ".pcov");
  265. TRACE("outfilename %s", outfilename);
  266. xc_coverager_save_cov(ZSTR_S(s), outfilename, *pcov TSRMLS_CC);
  267. zend_hash_move_forward_ex(XG(coverages), &pos);
  268. }
  269. efree(outfilename);
  270. }
  271. }
  272. /* }}} */
  273. static void xc_coverager_dump(zval *return_value TSRMLS_DC) /* {{{ */
  274. {
  275. coverager_t *pcov;
  276. HashPosition pos;
  277. if (XG(coverages)) {
  278. array_init(return_value);
  279. zend_hash_internal_pointer_reset_ex(XG(coverages), &pos);
  280. while (zend_hash_get_current_data_ex(XG(coverages), (void **) &pcov, &pos) == SUCCESS) {
  281. zval *lines;
  282. long *phits;
  283. coverager_t cov;
  284. HashPosition pos2;
  285. zstr filename;
  286. uint size;
  287. cov = *pcov;
  288. zend_hash_get_current_key_ex(XG(coverages), &filename, &size, NULL, 0, &pos);
  289. MAKE_STD_ZVAL(lines);
  290. array_init(lines);
  291. zend_hash_internal_pointer_reset_ex(cov, &pos2);
  292. while (zend_hash_get_current_data_ex(cov, (void**)&phits, &pos2) == SUCCESS) {
  293. long hits = *phits;
  294. add_index_long(lines, pos2->h, hits >= 0 ? hits : 0);
  295. zend_hash_move_forward_ex(cov, &pos2);
  296. }
  297. add_assoc_zval_ex(return_value, ZSTR_S(filename), (uint) strlen(ZSTR_S(filename)) + 1, lines);
  298. zend_hash_move_forward_ex(XG(coverages), &pos);
  299. }
  300. }
  301. else {
  302. RETVAL_NULL();
  303. }
  304. }
  305. /* }}} */
  306. static PHP_RSHUTDOWN_FUNCTION(xcache_coverager) /* {{{ */
  307. {
  308. if (XG(coverager)) {
  309. xc_coverager_autodump(TSRMLS_C);
  310. xc_coverager_cleanup(TSRMLS_C);
  311. }
  312. return SUCCESS;
  313. }
  314. /* }}} */
  315. /* helper func to store hits into coverages */
  316. static coverager_t xc_coverager_get(const char *filename TSRMLS_DC) /* {{{ */
  317. {
  318. uint len = (uint) strlen(filename) + 1;
  319. coverager_t cov, *pcov;
  320. if (zend_u_hash_find(XG(coverages), IS_STRING, filename, len, (void **) &pcov) == SUCCESS) {
  321. TRACE("got coverage %s %p", filename, (void *) *pcov);
  322. return *pcov;
  323. }
  324. else {
  325. cov = emalloc(sizeof(HashTable));
  326. zend_hash_init(cov, 0, NULL, NULL, 0);
  327. zend_u_hash_add(XG(coverages), IS_STRING, filename, len, (void **) &cov, sizeof(cov), NULL);
  328. TRACE("new coverage %s %p", filename, (void *) cov);
  329. return cov;
  330. }
  331. }
  332. /* }}} */
  333. static void xc_coverager_add_hits(HashTable *cov, long line, long hits TSRMLS_DC) /* {{{ */
  334. {
  335. long *poldhits;
  336. if (line == 0) {
  337. return;
  338. }
  339. if (zend_hash_index_find(cov, line, (void**)&poldhits) == SUCCESS) {
  340. if (hits == -1) {
  341. /* OPTIMIZE: -1 == init-ing, but it's already initized */
  342. return;
  343. }
  344. if (*poldhits != -1) {
  345. hits += *poldhits;
  346. }
  347. }
  348. zend_hash_index_update(cov, line, &hits, sizeof(hits), NULL);
  349. }
  350. /* }}} */
  351. static int xc_coverager_get_op_array_size_no_tail(zend_op_array *op_array) /* {{{ */
  352. {
  353. zend_uint last = op_array->last;
  354. do {
  355. next_op:
  356. if (last == 0) {
  357. break;
  358. }
  359. switch (op_array->opcodes[last - 1].opcode) {
  360. #ifdef ZEND_HANDLE_EXCEPTION
  361. case ZEND_HANDLE_EXCEPTION:
  362. #endif
  363. case ZEND_RETURN:
  364. case ZEND_EXT_STMT:
  365. --last;
  366. goto next_op;
  367. }
  368. } while (0);
  369. return last;
  370. }
  371. /* }}} */
  372. /* prefill */
  373. static int xc_coverager_init_op_array(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  374. {
  375. zend_uint size;
  376. coverager_t cov;
  377. zend_uint i;
  378. if (op_array->type != ZEND_USER_FUNCTION) {
  379. return 0;
  380. }
  381. size = xc_coverager_get_op_array_size_no_tail(op_array);
  382. cov = xc_coverager_get(op_array->filename TSRMLS_CC);
  383. for (i = 0; i < size; i ++) {
  384. switch (op_array->opcodes[i].opcode) {
  385. case ZEND_EXT_STMT:
  386. #if 0
  387. case ZEND_EXT_FCALL_BEGIN:
  388. case ZEND_EXT_FCALL_END:
  389. #endif
  390. xc_coverager_add_hits(cov, op_array->opcodes[i].lineno, -1 TSRMLS_CC);
  391. break;
  392. }
  393. }
  394. return 0;
  395. }
  396. /* }}} */
  397. static void xc_coverager_init_compile_result(zend_op_array *op_array TSRMLS_DC) /* {{{ */
  398. {
  399. xc_compile_result_t cr;
  400. xc_compile_result_init_cur(&cr, op_array TSRMLS_CC);
  401. xc_apply_op_array(&cr, (apply_func_t) xc_coverager_init_op_array TSRMLS_CC);
  402. xc_compile_result_free(&cr);
  403. }
  404. /* }}} */
  405. static zend_op_array *xc_compile_file_for_coverage(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
  406. {
  407. zend_op_array *op_array;
  408. op_array = old_compile_file(h, type TSRMLS_CC);
  409. if (op_array) {
  410. if (XG(coverager)) {
  411. xc_coverager_initenv(TSRMLS_C);
  412. xc_coverager_init_compile_result(op_array TSRMLS_CC);
  413. }
  414. }
  415. return op_array;
  416. }
  417. /* }}} */
  418. /* hits */
  419. static void xc_coverager_handle_ext_stmt(zend_op_array *op_array, zend_uchar op) /* {{{ */
  420. {
  421. TSRMLS_FETCH();
  422. if (XG(coverages) && XG(coverager_started)) {
  423. int size = xc_coverager_get_op_array_size_no_tail(op_array);
  424. int oplineno = (int) ((*EG(opline_ptr)) - op_array->opcodes);
  425. if (oplineno < size) {
  426. xc_coverager_add_hits(xc_coverager_get(op_array->filename TSRMLS_CC), (*EG(opline_ptr))->lineno, 1 TSRMLS_CC);
  427. }
  428. }
  429. }
  430. /* }}} */
  431. /* user api */
  432. /* {{{ proto array xcache_coverager_decode(string data)
  433. * decode specified data which is saved by auto dumper to array
  434. */
  435. #ifdef ZEND_BEGIN_ARG_INFO_EX
  436. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_coverager_decode, 0, 0, 1)
  437. ZEND_ARG_INFO(0, data)
  438. ZEND_END_ARG_INFO()
  439. #else
  440. static unsigned char arginfo_xcache_coverager_decode[] = { 1, BYREF_NONE };
  441. #endif
  442. PHP_FUNCTION(xcache_coverager_decode)
  443. {
  444. char *str;
  445. int len;
  446. long *p;
  447. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) {
  448. return;
  449. }
  450. array_init(return_value);
  451. p = (long*) str;
  452. len -= (int) sizeof(long);
  453. if (len < 0) {
  454. return;
  455. }
  456. if (*p++ != PCOV_HEADER_MAGIC) {
  457. TRACE("%s", "wrong magic in xcache_coverager_decode");
  458. return;
  459. }
  460. for (; len >= (int) sizeof(long) * 2; len -= (int) sizeof(long) * 2, p += 2) {
  461. add_index_long(return_value, p[0], p[1] < 0 ? 0 : p[1]);
  462. }
  463. }
  464. /* }}} */
  465. /* {{{ proto void xcache_coverager_start([bool clean = true])
  466. * starts coverager data collecting
  467. */
  468. #ifdef ZEND_BEGIN_ARG_INFO_EX
  469. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_coverager_start, 0, 0, 0)
  470. ZEND_ARG_INFO(0, clean)
  471. ZEND_END_ARG_INFO()
  472. #else
  473. static unsigned char arginfo_xcache_coverager_start[] = { 1, BYREF_NONE };
  474. #endif
  475. PHP_FUNCTION(xcache_coverager_start)
  476. {
  477. zend_bool clean = 1;
  478. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean) == FAILURE) {
  479. return;
  480. }
  481. if (clean) {
  482. xc_coverager_clean(TSRMLS_C);
  483. }
  484. if (XG(coverager)) {
  485. xc_coverager_start(TSRMLS_C);
  486. }
  487. else {
  488. php_error(E_WARNING, "You can only start coverager after you set 'xcache.coverager' to 'On' in ini");
  489. }
  490. }
  491. /* }}} */
  492. /* {{{ proto void xcache_coverager_stop([bool clean = false])
  493. * stop coverager data collecting
  494. */
  495. #ifdef ZEND_BEGIN_ARG_INFO_EX
  496. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_coverager_stop, 0, 0, 0)
  497. ZEND_ARG_INFO(0, clean)
  498. ZEND_END_ARG_INFO()
  499. #else
  500. static unsigned char arginfo_xcache_coverager_stop[] = { 1, BYREF_NONE };
  501. #endif
  502. PHP_FUNCTION(xcache_coverager_stop)
  503. {
  504. zend_bool clean = 0;
  505. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean) == FAILURE) {
  506. return;
  507. }
  508. xc_coverager_stop(TSRMLS_C);
  509. if (clean) {
  510. xc_coverager_clean(TSRMLS_C);
  511. }
  512. }
  513. /* }}} */
  514. /* {{{ proto array xcache_coverager_get([bool clean = false])
  515. * get coverager data collected
  516. */
  517. #ifdef ZEND_BEGIN_ARG_INFO_EX
  518. ZEND_BEGIN_ARG_INFO_EX(arginfo_xcache_coverager_get, 0, 0, 0)
  519. ZEND_ARG_INFO(0, clean)
  520. ZEND_END_ARG_INFO()
  521. #else
  522. static unsigned char arginfo_xcache_coverager_get[] = { 1, BYREF_NONE };
  523. #endif
  524. PHP_FUNCTION(xcache_coverager_get)
  525. {
  526. zend_bool clean = 0;
  527. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean) == FAILURE) {
  528. return;
  529. }
  530. xc_coverager_dump(return_value TSRMLS_CC);
  531. if (clean) {
  532. xc_coverager_clean(TSRMLS_C);
  533. }
  534. }
  535. /* }}} */
  536. static zend_function_entry xcache_coverager_functions[] = /* {{{ */
  537. {
  538. PHP_FE(xcache_coverager_decode, arginfo_xcache_coverager_decode)
  539. PHP_FE(xcache_coverager_start, arginfo_xcache_coverager_start)
  540. PHP_FE(xcache_coverager_stop, arginfo_xcache_coverager_stop)
  541. PHP_FE(xcache_coverager_get, arginfo_xcache_coverager_get)
  542. PHP_FE_END
  543. };
  544. /* }}} */
  545. static int xc_coverager_zend_startup(zend_extension *extension) /* {{{ */
  546. {
  547. old_compile_file = zend_compile_file;
  548. zend_compile_file = xc_compile_file_for_coverage;
  549. return SUCCESS;
  550. }
  551. /* }}} */
  552. static void xc_coverager_zend_shutdown(zend_extension *extension) /* {{{ */
  553. {
  554. /* empty */
  555. }
  556. /* }}} */
  557. static void xc_statement_handler(zend_op_array *op_array) /* {{{ */
  558. {
  559. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_STMT);
  560. }
  561. /* }}} */
  562. static void xc_fcall_begin_handler(zend_op_array *op_array) /* {{{ */
  563. {
  564. #if 0
  565. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_BEGIN);
  566. #endif
  567. }
  568. /* }}} */
  569. static void xc_fcall_end_handler(zend_op_array *op_array) /* {{{ */
  570. {
  571. #if 0
  572. xc_coverager_handle_ext_stmt(op_array, ZEND_EXT_FCALL_END);
  573. #endif
  574. }
  575. /* }}} */
  576. /* {{{ zend extension definition structure */
  577. static zend_extension xc_coverager_zend_extension_entry = {
  578. XCACHE_NAME " Coverager",
  579. XCACHE_VERSION,
  580. XCACHE_AUTHOR,
  581. XCACHE_URL,
  582. XCACHE_COPYRIGHT,
  583. xc_coverager_zend_startup,
  584. xc_coverager_zend_shutdown,
  585. NULL, /* activate_func_t */
  586. NULL, /* deactivate_func_t */
  587. NULL, /* message_handler_func_t */
  588. NULL, /* statement_handler_func_t */
  589. xc_statement_handler,
  590. xc_fcall_begin_handler,
  591. xc_fcall_end_handler,
  592. NULL, /* op_array_ctor_func_t */
  593. NULL, /* op_array_dtor_func_t */
  594. STANDARD_ZEND_EXTENSION_PROPERTIES
  595. };
  596. /* }}} */
  597. /* {{{ PHP_INI */
  598. PHP_INI_BEGIN()
  599. STD_PHP_INI_BOOLEAN("xcache.coverager", "0", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, coverager, zend_xcache_globals, xcache_globals)
  600. STD_PHP_INI_BOOLEAN("xcache.coverager_autostart", "1", PHP_INI_SYSTEM|PHP_INI_PERDIR, OnUpdateBool, coverager_autostart, zend_xcache_globals, xcache_globals)
  601. PHP_INI_ENTRY1 ("xcache.coveragedump_directory", "", PHP_INI_SYSTEM, xcache_OnUpdateDummy, NULL)
  602. PHP_INI_END()
  603. /* }}} */
  604. static PHP_MINFO_FUNCTION(xcache_coverager) /* {{{ */
  605. {
  606. char *covdumpdir;
  607. php_info_print_table_start();
  608. php_info_print_table_row(2, "XCache Coverager Module", "enabled");
  609. if (cfg_get_string("xcache.coveragedump_directory", &covdumpdir) != SUCCESS || !covdumpdir[0]) {
  610. covdumpdir = NULL;
  611. }
  612. php_info_print_table_row(2, "Coverage Started", XG(coverager_started) && covdumpdir ? "On" : "Off");
  613. php_info_print_table_end();
  614. DISPLAY_INI_ENTRIES();
  615. }
  616. /* }}} */
  617. static PHP_MINIT_FUNCTION(xcache_coverager) /* {{{ */
  618. {
  619. REGISTER_INI_ENTRIES();
  620. if (cfg_get_string("xcache.coveragedump_directory", &xc_coveragedump_dir) == SUCCESS && xc_coveragedump_dir) {
  621. size_t len;
  622. xc_coveragedump_dir = pestrdup(xc_coveragedump_dir, 1);
  623. len = strlen(xc_coveragedump_dir);
  624. if (len) {
  625. if (xc_coveragedump_dir[len - 1] == '/') {
  626. xc_coveragedump_dir[len - 1] = '\0';
  627. }
  628. }
  629. if (!strlen(xc_coveragedump_dir)) {
  630. pefree(xc_coveragedump_dir, 1);
  631. xc_coveragedump_dir = NULL;
  632. }
  633. }
  634. return xcache_zend_extension_add(&xc_coverager_zend_extension_entry, 0);
  635. }
  636. /* }}} */
  637. static PHP_MSHUTDOWN_FUNCTION(xcache_coverager) /* {{{ */
  638. {
  639. if (old_compile_file && zend_compile_file == xc_compile_file_for_coverage) {
  640. zend_compile_file = old_compile_file;
  641. old_compile_file = NULL;
  642. }
  643. if (xc_coveragedump_dir) {
  644. pefree(xc_coveragedump_dir, 1);
  645. xc_coveragedump_dir = NULL;
  646. }
  647. UNREGISTER_INI_ENTRIES();
  648. return xcache_zend_extension_remove(&xc_coverager_zend_extension_entry);
  649. }
  650. /* }}} */
  651. static zend_module_entry xcache_coverager_module_entry = { /* {{{ */
  652. STANDARD_MODULE_HEADER,
  653. XCACHE_NAME " Coverager",
  654. xcache_coverager_functions,
  655. PHP_MINIT(xcache_coverager),
  656. PHP_MSHUTDOWN(xcache_coverager),
  657. PHP_RINIT(xcache_coverager),
  658. PHP_RSHUTDOWN(xcache_coverager),
  659. PHP_MINFO(xcache_coverager),
  660. XCACHE_VERSION,
  661. #ifdef PHP_GINIT
  662. NO_MODULE_GLOBALS,
  663. #endif
  664. #ifdef ZEND_ENGINE_2
  665. NULL,
  666. #else
  667. NULL,
  668. NULL,
  669. #endif
  670. STANDARD_MODULE_PROPERTIES_EX
  671. };
  672. /* }}} */
  673. int xc_coverager_startup_module() /* {{{ */
  674. {
  675. return zend_startup_module(&xcache_coverager_module_entry);
  676. }
  677. /* }}} */