observer.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) The PHP Group |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to version 3.01 of the PHP license, |
  6. | that is bundled with this package in the file LICENSE, and is |
  7. | available through the world-wide-web at the following url: |
  8. | https://www.php.net/license/3_01.txt |
  9. | If you did not receive a copy of the PHP license and are unable to |
  10. | obtain it through the world-wide-web, please send a note to |
  11. | license@php.net so we can mail you a copy immediately. |
  12. +----------------------------------------------------------------------+
  13. | Author: |
  14. +----------------------------------------------------------------------+
  15. */
  16. #include "php.h"
  17. #include "php_test.h"
  18. #include "observer.h"
  19. #include "zend_observer.h"
  20. #include "zend_smart_str.h"
  21. #include "ext/standard/php_var.h"
  22. static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execute_data);
  23. static int observer_show_opcode_in_user_handler(zend_execute_data *execute_data)
  24. {
  25. if (ZT_G(observer_show_output)) {
  26. php_printf("%*s<!-- opcode: '%s' in user handler -->\n", 2 * ZT_G(observer_nesting_depth), "", zend_get_opcode_name(EX(opline)->opcode));
  27. }
  28. return ZEND_USER_OPCODE_DISPATCH;
  29. }
  30. static void observer_set_user_opcode_handler(const char *opcode_names, user_opcode_handler_t handler)
  31. {
  32. const char *s = NULL, *e = opcode_names;
  33. while (1) {
  34. if (*e == ' ' || *e == ',' || *e == '\0') {
  35. if (s) {
  36. zend_uchar opcode = zend_get_opcode_id(s, e - s);
  37. if (opcode <= ZEND_VM_LAST_OPCODE) {
  38. zend_set_user_opcode_handler(opcode, handler);
  39. } else {
  40. zend_error(E_WARNING, "Invalid opcode name %.*s", (int) (e - s), e);
  41. }
  42. s = NULL;
  43. }
  44. } else {
  45. if (!s) {
  46. s = e;
  47. }
  48. }
  49. if (*e == '\0') {
  50. break;
  51. }
  52. e++;
  53. }
  54. }
  55. static void observer_show_opcode(zend_execute_data *execute_data)
  56. {
  57. if (!ZT_G(observer_show_opcode)) {
  58. return;
  59. }
  60. php_printf("%*s<!-- opcode: '%s' -->\n", 2 * ZT_G(observer_nesting_depth), "", zend_get_opcode_name(EX(opline)->opcode));
  61. }
  62. static void observer_begin(zend_execute_data *execute_data)
  63. {
  64. if (!ZT_G(observer_show_output)) {
  65. return;
  66. }
  67. if (execute_data->func && execute_data->func->common.function_name) {
  68. if (execute_data->func->common.scope) {
  69. php_printf("%*s<%s::%s>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->common.scope->name), ZSTR_VAL(execute_data->func->common.function_name));
  70. } else {
  71. php_printf("%*s<%s>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->common.function_name));
  72. }
  73. } else {
  74. php_printf("%*s<file '%s'>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->op_array.filename));
  75. }
  76. ZT_G(observer_nesting_depth)++;
  77. observer_show_opcode(execute_data);
  78. }
  79. static void get_retval_info(zval *retval, smart_str *buf)
  80. {
  81. if (!ZT_G(observer_show_return_type) && !ZT_G(observer_show_return_value)) {
  82. return;
  83. }
  84. smart_str_appendc(buf, ':');
  85. if (retval == NULL) {
  86. smart_str_appendl(buf, "NULL", 4);
  87. } else if (ZT_G(observer_show_return_value)) {
  88. if (Z_TYPE_P(retval) == IS_OBJECT) {
  89. smart_str_appendl(buf, "object(", 7);
  90. smart_str_append(buf, Z_OBJCE_P(retval)->name);
  91. smart_str_appendl(buf, ")#", 2);
  92. smart_str_append_long(buf, Z_OBJ_HANDLE_P(retval));
  93. } else {
  94. php_var_export_ex(retval, 2 * ZT_G(observer_nesting_depth) + 3, buf);
  95. }
  96. } else if (ZT_G(observer_show_return_type)) {
  97. smart_str_appends(buf, zend_zval_type_name(retval));
  98. }
  99. smart_str_0(buf);
  100. }
  101. static void observer_end(zend_execute_data *execute_data, zval *retval)
  102. {
  103. if (!ZT_G(observer_show_output)) {
  104. return;
  105. }
  106. if (EG(exception)) {
  107. php_printf("%*s<!-- Exception: %s -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(EG(exception)->ce->name));
  108. }
  109. observer_show_opcode(execute_data);
  110. ZT_G(observer_nesting_depth)--;
  111. if (execute_data->func && execute_data->func->common.function_name) {
  112. smart_str retval_info = {0};
  113. get_retval_info(retval, &retval_info);
  114. if (execute_data->func->common.scope) {
  115. php_printf("%*s</%s::%s%s>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->common.scope->name), ZSTR_VAL(execute_data->func->common.function_name), retval_info.s ? ZSTR_VAL(retval_info.s) : "");
  116. } else {
  117. php_printf("%*s</%s%s>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->common.function_name), retval_info.s ? ZSTR_VAL(retval_info.s) : "");
  118. }
  119. smart_str_free(&retval_info);
  120. } else {
  121. php_printf("%*s</file '%s'>\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(execute_data->func->op_array.filename));
  122. }
  123. }
  124. static void observer_show_init(zend_function *fbc)
  125. {
  126. if (fbc->common.function_name) {
  127. if (fbc->common.scope) {
  128. php_printf("%*s<!-- init %s::%s() -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
  129. } else {
  130. php_printf("%*s<!-- init %s() -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->common.function_name));
  131. }
  132. } else {
  133. php_printf("%*s<!-- init '%s' -->\n", 2 * ZT_G(observer_nesting_depth), "", ZSTR_VAL(fbc->op_array.filename));
  134. }
  135. }
  136. static void observer_show_init_backtrace(zend_execute_data *execute_data)
  137. {
  138. zend_execute_data *ex = execute_data;
  139. php_printf("%*s<!--\n", 2 * ZT_G(observer_nesting_depth), "");
  140. do {
  141. zend_function *fbc = ex->func;
  142. int indent = 2 * ZT_G(observer_nesting_depth) + 4;
  143. if (fbc->common.function_name) {
  144. if (fbc->common.scope) {
  145. php_printf("%*s%s::%s()\n", indent, "", ZSTR_VAL(fbc->common.scope->name), ZSTR_VAL(fbc->common.function_name));
  146. } else {
  147. php_printf("%*s%s()\n", indent, "", ZSTR_VAL(fbc->common.function_name));
  148. }
  149. } else {
  150. php_printf("%*s{main} %s\n", indent, "", ZSTR_VAL(fbc->op_array.filename));
  151. }
  152. } while ((ex = ex->prev_execute_data) != NULL);
  153. php_printf("%*s-->\n", 2 * ZT_G(observer_nesting_depth), "");
  154. }
  155. static zend_observer_fcall_handlers observer_fcall_init(zend_execute_data *execute_data)
  156. {
  157. zend_function *fbc = execute_data->func;
  158. if (ZT_G(observer_show_output)) {
  159. observer_show_init(fbc);
  160. if (ZT_G(observer_show_init_backtrace)) {
  161. observer_show_init_backtrace(execute_data);
  162. }
  163. observer_show_opcode(execute_data);
  164. }
  165. if (ZT_G(observer_observe_all)) {
  166. return (zend_observer_fcall_handlers){observer_begin, observer_end};
  167. } else if (fbc->common.function_name) {
  168. if (ZT_G(observer_observe_functions)) {
  169. return (zend_observer_fcall_handlers){observer_begin, observer_end};
  170. } else if (ZT_G(observer_observe_function_names) && zend_hash_exists(ZT_G(observer_observe_function_names), fbc->common.function_name)) {
  171. return (zend_observer_fcall_handlers){observer_begin, observer_end};
  172. }
  173. } else {
  174. if (ZT_G(observer_observe_includes)) {
  175. return (zend_observer_fcall_handlers){observer_begin, observer_end};
  176. }
  177. }
  178. return (zend_observer_fcall_handlers){NULL, NULL};
  179. }
  180. static void fiber_init_observer(zend_fiber_context *initializing) {
  181. if (ZT_G(observer_fiber_init)) {
  182. php_printf("<!-- alloc: %p -->\n", initializing);
  183. }
  184. }
  185. static void fiber_destroy_observer(zend_fiber_context *destroying) {
  186. if (ZT_G(observer_fiber_destroy)) {
  187. php_printf("<!-- destroy: %p -->\n", destroying);
  188. }
  189. }
  190. static void fiber_address_observer(zend_fiber_context *from, zend_fiber_context *to)
  191. {
  192. if (ZT_G(observer_fiber_switch)) {
  193. php_printf("<!-- switching from fiber %p to %p -->\n", from, to);
  194. }
  195. }
  196. static void fiber_enter_observer(zend_fiber_context *from, zend_fiber_context *to)
  197. {
  198. if (ZT_G(observer_fiber_switch)) {
  199. if (to->status == ZEND_FIBER_STATUS_INIT) {
  200. php_printf("<init '%p'>\n", to);
  201. } else if (to->kind == zend_ce_fiber) {
  202. zend_fiber *fiber = zend_fiber_from_context(to);
  203. if (fiber->caller != from) {
  204. return;
  205. }
  206. if (fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
  207. php_printf("<destroying '%p'>\n", to);
  208. } else if (to->status != ZEND_FIBER_STATUS_DEAD) {
  209. php_printf("<resume '%p'>\n", to);
  210. }
  211. }
  212. }
  213. }
  214. static void fiber_suspend_observer(zend_fiber_context *from, zend_fiber_context *to)
  215. {
  216. if (ZT_G(observer_fiber_switch)) {
  217. if (from->status == ZEND_FIBER_STATUS_DEAD) {
  218. zend_fiber *fiber = (from->kind == zend_ce_fiber) ? zend_fiber_from_context(from) : NULL;
  219. if (fiber && fiber->flags & ZEND_FIBER_FLAG_THREW) {
  220. php_printf("<threw '%p'>\n", from);
  221. } else if (fiber && fiber->flags & ZEND_FIBER_FLAG_DESTROYED) {
  222. php_printf("<destroyed '%p'>\n", from);
  223. } else {
  224. php_printf("<returned '%p'>\n", from);
  225. }
  226. } else if (from->kind == zend_ce_fiber) {
  227. zend_fiber *fiber = zend_fiber_from_context(from);
  228. if (fiber->caller == NULL) {
  229. php_printf("<suspend '%p'>\n", from);
  230. }
  231. }
  232. }
  233. }
  234. static ZEND_INI_MH(zend_test_observer_OnUpdateCommaList)
  235. {
  236. zend_array **p = (zend_array **) ZEND_INI_GET_ADDR();
  237. if (*p) {
  238. zend_hash_release(*p);
  239. }
  240. *p = NULL;
  241. if (new_value && ZSTR_LEN(new_value)) {
  242. *p = malloc(sizeof(HashTable));
  243. _zend_hash_init(*p, 8, ZVAL_PTR_DTOR, 1);
  244. const char *start = ZSTR_VAL(new_value), *ptr;
  245. while ((ptr = strchr(start, ','))) {
  246. zend_hash_str_add_empty_element(*p, start, ptr - start);
  247. start = ptr + 1;
  248. }
  249. zend_hash_str_add_empty_element(*p, start, ZSTR_VAL(new_value) + ZSTR_LEN(new_value) - start);
  250. }
  251. return SUCCESS;
  252. }
  253. PHP_INI_BEGIN()
  254. STD_PHP_INI_BOOLEAN("zend_test.observer.enabled", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_enabled, zend_zend_test_globals, zend_test_globals)
  255. STD_PHP_INI_BOOLEAN("zend_test.observer.show_output", "1", PHP_INI_SYSTEM, OnUpdateBool, observer_show_output, zend_zend_test_globals, zend_test_globals)
  256. STD_PHP_INI_BOOLEAN("zend_test.observer.observe_all", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_all, zend_zend_test_globals, zend_test_globals)
  257. STD_PHP_INI_BOOLEAN("zend_test.observer.observe_includes", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_includes, zend_zend_test_globals, zend_test_globals)
  258. STD_PHP_INI_BOOLEAN("zend_test.observer.observe_functions", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_observe_functions, zend_zend_test_globals, zend_test_globals)
  259. STD_PHP_INI_ENTRY("zend_test.observer.observe_function_names", "", PHP_INI_SYSTEM, zend_test_observer_OnUpdateCommaList, observer_observe_function_names, zend_zend_test_globals, zend_test_globals)
  260. STD_PHP_INI_BOOLEAN("zend_test.observer.show_return_type", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_return_type, zend_zend_test_globals, zend_test_globals)
  261. STD_PHP_INI_BOOLEAN("zend_test.observer.show_return_value", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_return_value, zend_zend_test_globals, zend_test_globals)
  262. STD_PHP_INI_BOOLEAN("zend_test.observer.show_init_backtrace", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_init_backtrace, zend_zend_test_globals, zend_test_globals)
  263. STD_PHP_INI_BOOLEAN("zend_test.observer.show_opcode", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_show_opcode, zend_zend_test_globals, zend_test_globals)
  264. STD_PHP_INI_ENTRY("zend_test.observer.show_opcode_in_user_handler", "", PHP_INI_SYSTEM, OnUpdateString, observer_show_opcode_in_user_handler, zend_zend_test_globals, zend_test_globals)
  265. STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_init", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_init, zend_zend_test_globals, zend_test_globals)
  266. STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_switch", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_switch, zend_zend_test_globals, zend_test_globals)
  267. STD_PHP_INI_BOOLEAN("zend_test.observer.fiber_destroy", "0", PHP_INI_SYSTEM, OnUpdateBool, observer_fiber_destroy, zend_zend_test_globals, zend_test_globals)
  268. PHP_INI_END()
  269. void zend_test_observer_init(INIT_FUNC_ARGS)
  270. {
  271. // Loading via dl() not supported with the observer API
  272. if (type != MODULE_TEMPORARY) {
  273. REGISTER_INI_ENTRIES();
  274. if (ZT_G(observer_enabled)) {
  275. zend_observer_fcall_register(observer_fcall_init);
  276. }
  277. } else {
  278. (void)ini_entries;
  279. }
  280. if (ZT_G(observer_enabled) && ZT_G(observer_show_opcode_in_user_handler)) {
  281. observer_set_user_opcode_handler(ZT_G(observer_show_opcode_in_user_handler), observer_show_opcode_in_user_handler);
  282. }
  283. if (ZT_G(observer_enabled)) {
  284. zend_observer_fiber_init_register(fiber_init_observer);
  285. zend_observer_fiber_switch_register(fiber_address_observer);
  286. zend_observer_fiber_switch_register(fiber_enter_observer);
  287. zend_observer_fiber_switch_register(fiber_suspend_observer);
  288. zend_observer_fiber_destroy_register(fiber_destroy_observer);
  289. }
  290. }
  291. void zend_test_observer_shutdown(SHUTDOWN_FUNC_ARGS)
  292. {
  293. if (type != MODULE_TEMPORARY) {
  294. UNREGISTER_INI_ENTRIES();
  295. }
  296. if (ZT_G(observer_observe_function_names)) {
  297. zend_hash_release(ZT_G(observer_observe_function_names));
  298. }
  299. }