phpdbg_info.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  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. | Authors: Felipe Pena <felipe@php.net> |
  14. | Authors: Joe Watkins <joe.watkins@live.co.uk> |
  15. | Authors: Bob Weinand <bwoebi@php.net> |
  16. +----------------------------------------------------------------------+
  17. */
  18. #include "php.h"
  19. #include "phpdbg.h"
  20. #include "phpdbg_utils.h"
  21. #include "phpdbg_info.h"
  22. #include "phpdbg_bp.h"
  23. #include "phpdbg_prompt.h"
  24. ZEND_EXTERN_MODULE_GLOBALS(phpdbg)
  25. #define PHPDBG_INFO_COMMAND_D(f, h, a, m, l, s, flags) \
  26. PHPDBG_COMMAND_D_EXP(f, h, a, m, l, s, &phpdbg_prompt_commands[13], flags)
  27. const phpdbg_command_t phpdbg_info_commands[] = {
  28. PHPDBG_INFO_COMMAND_D(break, "show breakpoints", 'b', info_break, NULL, 0, PHPDBG_ASYNC_SAFE),
  29. PHPDBG_INFO_COMMAND_D(files, "show included files", 'F', info_files, NULL, 0, PHPDBG_ASYNC_SAFE),
  30. PHPDBG_INFO_COMMAND_D(classes, "show loaded classes", 'c', info_classes, NULL, 0, PHPDBG_ASYNC_SAFE),
  31. PHPDBG_INFO_COMMAND_D(funcs, "show loaded classes", 'f', info_funcs, NULL, 0, PHPDBG_ASYNC_SAFE),
  32. PHPDBG_INFO_COMMAND_D(error, "show last error", 'e', info_error, NULL, 0, PHPDBG_ASYNC_SAFE),
  33. PHPDBG_INFO_COMMAND_D(constants, "show user defined constants", 'd', info_constants, NULL, 0, PHPDBG_ASYNC_SAFE),
  34. PHPDBG_INFO_COMMAND_D(vars, "show active variables", 'v', info_vars, NULL, 0, PHPDBG_ASYNC_SAFE),
  35. PHPDBG_INFO_COMMAND_D(globals, "show superglobals", 'g', info_globals, NULL, 0, PHPDBG_ASYNC_SAFE),
  36. PHPDBG_INFO_COMMAND_D(literal, "show active literal constants", 'l', info_literal, NULL, 0, PHPDBG_ASYNC_SAFE),
  37. PHPDBG_INFO_COMMAND_D(memory, "show memory manager stats", 'm', info_memory, NULL, 0, PHPDBG_ASYNC_SAFE),
  38. PHPDBG_END_COMMAND
  39. };
  40. PHPDBG_INFO(break) /* {{{ */
  41. {
  42. phpdbg_print_breakpoints(PHPDBG_BREAK_FILE);
  43. phpdbg_print_breakpoints(PHPDBG_BREAK_SYM);
  44. phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD);
  45. phpdbg_print_breakpoints(PHPDBG_BREAK_OPLINE);
  46. phpdbg_print_breakpoints(PHPDBG_BREAK_FILE_OPLINE);
  47. phpdbg_print_breakpoints(PHPDBG_BREAK_FUNCTION_OPLINE);
  48. phpdbg_print_breakpoints(PHPDBG_BREAK_METHOD_OPLINE);
  49. phpdbg_print_breakpoints(PHPDBG_BREAK_COND);
  50. phpdbg_print_breakpoints(PHPDBG_BREAK_OPCODE);
  51. return SUCCESS;
  52. } /* }}} */
  53. PHPDBG_INFO(files) /* {{{ */
  54. {
  55. zend_string *fname;
  56. phpdbg_try_access {
  57. phpdbg_notice("Included files: %d", zend_hash_num_elements(&EG(included_files)));
  58. } phpdbg_catch_access {
  59. phpdbg_error("Could not fetch included file count, invalid data source");
  60. return SUCCESS;
  61. } phpdbg_end_try_access();
  62. phpdbg_try_access {
  63. ZEND_HASH_FOREACH_STR_KEY(&EG(included_files), fname) {
  64. phpdbg_writeln("File: %s", ZSTR_VAL(fname));
  65. } ZEND_HASH_FOREACH_END();
  66. } phpdbg_catch_access {
  67. phpdbg_error("Could not fetch file name, invalid data source, aborting included file listing");
  68. } phpdbg_end_try_access();
  69. return SUCCESS;
  70. } /* }}} */
  71. PHPDBG_INFO(error) /* {{{ */
  72. {
  73. if (PG(last_error_message)) {
  74. phpdbg_try_access {
  75. phpdbg_writeln("Last error: %s at %s line %d",
  76. ZSTR_VAL(PG(last_error_message)),
  77. ZSTR_VAL(PG(last_error_file)),
  78. PG(last_error_lineno));
  79. } phpdbg_catch_access {
  80. phpdbg_notice("No error found!");
  81. } phpdbg_end_try_access();
  82. } else {
  83. phpdbg_notice("No error found!");
  84. }
  85. return SUCCESS;
  86. } /* }}} */
  87. PHPDBG_INFO(constants) /* {{{ */
  88. {
  89. HashTable consts;
  90. zend_constant *data;
  91. zend_hash_init(&consts, 8, NULL, NULL, 0);
  92. if (EG(zend_constants)) {
  93. phpdbg_try_access {
  94. ZEND_HASH_FOREACH_PTR(EG(zend_constants), data) {
  95. if (ZEND_CONSTANT_MODULE_NUMBER(data) == PHP_USER_CONSTANT) {
  96. zend_hash_update_ptr(&consts, data->name, data);
  97. }
  98. } ZEND_HASH_FOREACH_END();
  99. } phpdbg_catch_access {
  100. phpdbg_error("Cannot fetch all the constants, invalid data source");
  101. } phpdbg_end_try_access();
  102. }
  103. phpdbg_notice("User-defined constants (%d)", zend_hash_num_elements(&consts));
  104. if (zend_hash_num_elements(&consts)) {
  105. phpdbg_out("Address Refs Type Constant\n");
  106. ZEND_HASH_FOREACH_PTR(&consts, data) {
  107. #define VARIABLEINFO(msg, ...) \
  108. phpdbg_writeln( \
  109. "%-18p %-7d %-9s %.*s" msg, &data->value, \
  110. Z_REFCOUNTED(data->value) ? Z_REFCOUNT(data->value) : 1, \
  111. zend_get_type_by_const(Z_TYPE(data->value)), \
  112. (int) ZSTR_LEN(data->name), ZSTR_VAL(data->name), ##__VA_ARGS__)
  113. switch (Z_TYPE(data->value)) {
  114. case IS_STRING:
  115. phpdbg_try_access {
  116. VARIABLEINFO("\nstring (%zd) \"%.*s%s\"", Z_STRLEN(data->value), Z_STRLEN(data->value) < 255 ? (int) Z_STRLEN(data->value) : 255, Z_STRVAL(data->value), Z_STRLEN(data->value) > 255 ? "..." : "");
  117. } phpdbg_catch_access {
  118. VARIABLEINFO("");
  119. } phpdbg_end_try_access();
  120. break;
  121. case IS_TRUE:
  122. VARIABLEINFO("\nbool (true)");
  123. break;
  124. case IS_FALSE:
  125. VARIABLEINFO("\nbool (false)");
  126. break;
  127. case IS_LONG:
  128. VARIABLEINFO("\nint ("ZEND_LONG_FMT")", Z_LVAL(data->value));
  129. break;
  130. case IS_DOUBLE:
  131. VARIABLEINFO("\ndouble (%lf)", Z_DVAL(data->value));
  132. break;
  133. default:
  134. VARIABLEINFO("");
  135. #undef VARIABLEINFO
  136. }
  137. } ZEND_HASH_FOREACH_END();
  138. }
  139. return SUCCESS;
  140. } /* }}} */
  141. static int phpdbg_arm_auto_global(zval *ptrzv) {
  142. zend_auto_global *auto_global = Z_PTR_P(ptrzv);
  143. if (auto_global->armed) {
  144. if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
  145. phpdbg_notice("Cannot show information about superglobal variable %.*s", (int) ZSTR_LEN(auto_global->name), ZSTR_VAL(auto_global->name));
  146. } else {
  147. auto_global->armed = auto_global->auto_global_callback(auto_global->name);
  148. }
  149. }
  150. return 0;
  151. }
  152. static int phpdbg_print_symbols(bool show_globals) {
  153. HashTable vars;
  154. zend_array *symtable;
  155. zend_string *var;
  156. zval *data;
  157. if (!EG(current_execute_data) || !EG(current_execute_data)->func) {
  158. phpdbg_error("No active op array!");
  159. return SUCCESS;
  160. }
  161. if (show_globals) {
  162. /* that array should only be manipulated during init, so safe for async access during execution */
  163. zend_hash_apply(CG(auto_globals), (apply_func_t) phpdbg_arm_auto_global);
  164. symtable = &EG(symbol_table);
  165. } else if (!(symtable = zend_rebuild_symbol_table())) {
  166. phpdbg_error("No active symbol table!");
  167. return SUCCESS;
  168. }
  169. zend_hash_init(&vars, 8, NULL, NULL, 0);
  170. phpdbg_try_access {
  171. ZEND_HASH_FOREACH_STR_KEY_VAL(symtable, var, data) {
  172. if (zend_is_auto_global(var) ^ !show_globals) {
  173. zend_hash_update(&vars, var, data);
  174. }
  175. } ZEND_HASH_FOREACH_END();
  176. } phpdbg_catch_access {
  177. phpdbg_error("Cannot fetch all data from the symbol table, invalid data source");
  178. } phpdbg_end_try_access();
  179. if (show_globals) {
  180. phpdbg_notice("Superglobal variables (%d)", zend_hash_num_elements(&vars));
  181. } else {
  182. zend_op_array *ops = &EG(current_execute_data)->func->op_array;
  183. if (ops->function_name) {
  184. if (ops->scope) {
  185. phpdbg_notice("Variables in %s::%s() (%d)", ops->scope->name->val, ops->function_name->val, zend_hash_num_elements(&vars));
  186. } else {
  187. phpdbg_notice("Variables in %s() (%d)", ZSTR_VAL(ops->function_name), zend_hash_num_elements(&vars));
  188. }
  189. } else {
  190. if (ops->filename) {
  191. phpdbg_notice("Variables in %s (%d)", ZSTR_VAL(ops->filename), zend_hash_num_elements(&vars));
  192. } else {
  193. phpdbg_notice("Variables @ %p (%d)", ops, zend_hash_num_elements(&vars));
  194. }
  195. }
  196. }
  197. if (zend_hash_num_elements(&vars)) {
  198. phpdbg_out("Address Refs Type Variable\n");
  199. ZEND_HASH_FOREACH_STR_KEY_VAL(&vars, var, data) {
  200. phpdbg_try_access {
  201. const char *isref = "";
  202. #define VARIABLEINFO(msg, ...) \
  203. phpdbg_writeln( \
  204. "%-18p %-7d %-9s %s$%.*s" msg, data, Z_REFCOUNTED_P(data) ? Z_REFCOUNT_P(data) : 1, zend_get_type_by_const(Z_TYPE_P(data)), isref, (int) ZSTR_LEN(var), ZSTR_VAL(var), ##__VA_ARGS__)
  205. retry_switch:
  206. switch (Z_TYPE_P(data)) {
  207. case IS_RESOURCE:
  208. phpdbg_try_access {
  209. const char *type = zend_rsrc_list_get_rsrc_type(Z_RES_P(data));
  210. VARIABLEINFO("\n|-------(typeof)------> (%s)\n", type ? type : "unknown");
  211. } phpdbg_catch_access {
  212. VARIABLEINFO("\n|-------(typeof)------> (unknown)\n");
  213. } phpdbg_end_try_access();
  214. break;
  215. case IS_OBJECT:
  216. phpdbg_try_access {
  217. VARIABLEINFO("\n|-----(instanceof)----> (%s)\n", ZSTR_VAL(Z_OBJCE_P(data)->name));
  218. } phpdbg_catch_access {
  219. VARIABLEINFO("\n|-----(instanceof)----> (unknown)\n");
  220. } phpdbg_end_try_access();
  221. break;
  222. case IS_STRING:
  223. phpdbg_try_access {
  224. VARIABLEINFO("\nstring (%zd) \"%.*s%s\"", Z_STRLEN_P(data), Z_STRLEN_P(data) < 255 ? (int) Z_STRLEN_P(data) : 255, Z_STRVAL_P(data), Z_STRLEN_P(data) > 255 ? "..." : "");
  225. } phpdbg_catch_access {
  226. VARIABLEINFO("");
  227. } phpdbg_end_try_access();
  228. break;
  229. case IS_TRUE:
  230. VARIABLEINFO("\nbool (true)");
  231. break;
  232. case IS_FALSE:
  233. VARIABLEINFO("\nbool (false)");
  234. break;
  235. case IS_LONG:
  236. VARIABLEINFO("\nint ("ZEND_LONG_FMT")", Z_LVAL_P(data));
  237. break;
  238. case IS_DOUBLE:
  239. VARIABLEINFO("\ndouble (%lf)", Z_DVAL_P(data));
  240. break;
  241. case IS_REFERENCE:
  242. isref = "&";
  243. data = Z_REFVAL_P(data);
  244. goto retry_switch;
  245. case IS_INDIRECT:
  246. data = Z_INDIRECT_P(data);
  247. goto retry_switch;
  248. default:
  249. VARIABLEINFO("");
  250. }
  251. #undef VARIABLEINFO
  252. } phpdbg_catch_access {
  253. phpdbg_writeln("%p\tn/a\tn/a\t$%s", data, ZSTR_VAL(var));
  254. } phpdbg_end_try_access();
  255. } ZEND_HASH_FOREACH_END();
  256. }
  257. zend_hash_destroy(&vars);
  258. return SUCCESS;
  259. } /* }}} */
  260. PHPDBG_INFO(vars) /* {{{ */
  261. {
  262. return phpdbg_print_symbols(0);
  263. }
  264. PHPDBG_INFO(globals) /* {{{ */
  265. {
  266. return phpdbg_print_symbols(1);
  267. }
  268. PHPDBG_INFO(literal) /* {{{ */
  269. {
  270. /* literals are assumed to not be manipulated during executing of their op_array and as such async safe */
  271. bool in_executor = PHPDBG_G(in_execution) && EG(current_execute_data) && EG(current_execute_data)->func;
  272. if (in_executor || PHPDBG_G(ops)) {
  273. zend_op_array *ops = in_executor ? &EG(current_execute_data)->func->op_array : PHPDBG_G(ops);
  274. int literal = 0, count = ops->last_literal - 1;
  275. if (ops->function_name) {
  276. if (ops->scope) {
  277. phpdbg_notice("Literal Constants in %s::%s() (%d)", ops->scope->name->val, ops->function_name->val, count);
  278. } else {
  279. phpdbg_notice("Literal Constants in %s() (%d)", ops->function_name->val, count);
  280. }
  281. } else {
  282. if (ops->filename) {
  283. phpdbg_notice("Literal Constants in %s (%d)", ZSTR_VAL(ops->filename), count);
  284. } else {
  285. phpdbg_notice("Literal Constants @ %p (%d)", ops, count);
  286. }
  287. }
  288. while (literal < ops->last_literal) {
  289. if (Z_TYPE(ops->literals[literal]) != IS_NULL) {
  290. phpdbg_write("|-------- C%u -------> [", literal);
  291. zend_print_zval(&ops->literals[literal], 0);
  292. phpdbg_out("]\n");
  293. }
  294. literal++;
  295. }
  296. } else {
  297. phpdbg_error("Not executing!");
  298. }
  299. return SUCCESS;
  300. } /* }}} */
  301. PHPDBG_INFO(memory) /* {{{ */
  302. {
  303. size_t used, real, peak_used, peak_real;
  304. zend_mm_heap *orig_heap = NULL;
  305. bool is_mm;
  306. if (PHPDBG_G(flags) & PHPDBG_IN_SIGNAL_HANDLER) {
  307. orig_heap = zend_mm_set_heap(phpdbg_original_heap_sigsafe_mem());
  308. }
  309. if ((is_mm = is_zend_mm())) {
  310. used = zend_memory_usage(0);
  311. real = zend_memory_usage(1);
  312. peak_used = zend_memory_peak_usage(0);
  313. peak_real = zend_memory_peak_usage(1);
  314. }
  315. if (orig_heap) {
  316. zend_mm_set_heap(orig_heap);
  317. }
  318. if (is_mm) {
  319. phpdbg_notice("Memory Manager Information");
  320. phpdbg_notice("Current");
  321. phpdbg_writeln( "|-------> Used:\t%.3f kB", (float) (used / 1024));
  322. phpdbg_writeln("|-------> Real:\t%.3f kB", (float) (real / 1024));
  323. phpdbg_notice("Peak");
  324. phpdbg_writeln("|-------> Used:\t%.3f kB", (float) (peak_used / 1024));
  325. phpdbg_writeln("|-------> Real:\t%.3f kB", (float) (peak_real / 1024));
  326. } else {
  327. phpdbg_error("Memory Manager Disabled!");
  328. }
  329. return SUCCESS;
  330. } /* }}} */
  331. static inline void phpdbg_print_class_name(zend_class_entry *ce) /* {{{ */
  332. {
  333. const char *visibility = ce->type == ZEND_USER_CLASS ? "User" : "Internal";
  334. const char *type = (ce->ce_flags & ZEND_ACC_INTERFACE) ? "Interface" : (ce->ce_flags & ZEND_ACC_ABSTRACT) ? "Abstract Class" : "Class";
  335. phpdbg_writeln("%s %s %.*s (%d)", visibility, type, (int) ZSTR_LEN(ce->name), ZSTR_VAL(ce->name), zend_hash_num_elements(&ce->function_table));
  336. } /* }}} */
  337. PHPDBG_INFO(classes) /* {{{ */
  338. {
  339. zend_class_entry *ce;
  340. HashTable classes;
  341. zend_hash_init(&classes, 8, NULL, NULL, 0);
  342. phpdbg_try_access {
  343. ZEND_HASH_FOREACH_PTR(EG(class_table), ce) {
  344. if (ce->type == ZEND_USER_CLASS) {
  345. zend_hash_next_index_insert_ptr(&classes, ce);
  346. }
  347. } ZEND_HASH_FOREACH_END();
  348. } phpdbg_catch_access {
  349. phpdbg_notice("Not all classes could be fetched, possibly invalid data source");
  350. } phpdbg_end_try_access();
  351. phpdbg_notice("User Classes (%d)", zend_hash_num_elements(&classes));
  352. /* once added, assume that classes are stable... until shutdown. */
  353. ZEND_HASH_FOREACH_PTR(&classes, ce) {
  354. phpdbg_print_class_name(ce);
  355. if (ce->parent) {
  356. zend_class_entry *pce;
  357. pce = ce->parent;
  358. do {
  359. phpdbg_out("|-------- ");
  360. phpdbg_print_class_name(pce);
  361. } while ((pce = pce->parent));
  362. }
  363. if (ce->info.user.filename) {
  364. phpdbg_writeln("|---- in %s on line %u", ZSTR_VAL(ce->info.user.filename), ce->info.user.line_start);
  365. } else {
  366. phpdbg_writeln("|---- no source code");
  367. }
  368. } ZEND_HASH_FOREACH_END();
  369. zend_hash_destroy(&classes);
  370. return SUCCESS;
  371. } /* }}} */
  372. PHPDBG_INFO(funcs) /* {{{ */
  373. {
  374. zend_function *zf;
  375. HashTable functions;
  376. zend_hash_init(&functions, 8, NULL, NULL, 0);
  377. phpdbg_try_access {
  378. ZEND_HASH_FOREACH_PTR(EG(function_table), zf) {
  379. if (zf->type == ZEND_USER_FUNCTION) {
  380. zend_hash_next_index_insert_ptr(&functions, zf);
  381. }
  382. } ZEND_HASH_FOREACH_END();
  383. } phpdbg_catch_access {
  384. phpdbg_notice("Not all functions could be fetched, possibly invalid data source");
  385. } phpdbg_end_try_access();
  386. phpdbg_notice("User Functions (%d)", zend_hash_num_elements(&functions));
  387. ZEND_HASH_FOREACH_PTR(&functions, zf) {
  388. zend_op_array *op_array = &zf->op_array;
  389. phpdbg_write("|-------- %s", op_array->function_name ? ZSTR_VAL(op_array->function_name) : "{main}");
  390. if (op_array->filename) {
  391. phpdbg_writeln(" in %s on line %d", ZSTR_VAL(op_array->filename), op_array->line_start);
  392. } else {
  393. phpdbg_writeln(" (no source code)");
  394. }
  395. } ZEND_HASH_FOREACH_END();
  396. zend_hash_destroy(&functions);
  397. return SUCCESS;
  398. } /* }}} */