PageRenderTime 58ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/xdebug_branch_info.c

http://github.com/derickr/xdebug
C | 476 lines | 377 code | 77 blank | 22 comment | 88 complexity | 0904708a0047be03bbab35a4753ec2e4 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. /*
  2. +----------------------------------------------------------------------+
  3. | Copyright (c) 1997-2016 Derick Rethans |
  4. +----------------------------------------------------------------------+
  5. | This source file is subject to the 2-Clause BSD license which is |
  6. | available through the LICENSE file, or online at |
  7. | http://opensource.org/licenses/bsd-license.php |
  8. +----------------------------------------------------------------------+
  9. | Authors: Derick Rethans <derick@derickrethans.nl> |
  10. +----------------------------------------------------------------------+
  11. */
  12. #include <stdlib.h>
  13. #include <math.h>
  14. #include "php_xdebug.h"
  15. #include "xdebug_str.h"
  16. #include "xdebug_hash.h"
  17. ZEND_EXTERN_MODULE_GLOBALS(xdebug)
  18. xdebug_branch_info *xdebug_branch_info_create(unsigned int size)
  19. {
  20. xdebug_branch_info *tmp;
  21. tmp = calloc(1, sizeof(xdebug_branch_info));
  22. tmp->size = size;
  23. tmp->branches = calloc(size, sizeof(xdebug_branch));
  24. tmp->entry_points = xdebug_set_create(size);
  25. tmp->starts = xdebug_set_create(size);
  26. tmp->ends = xdebug_set_create(size);
  27. tmp->path_info.paths_count = 0;
  28. tmp->path_info.paths_size = 0;
  29. tmp->path_info.paths = NULL;
  30. return tmp;
  31. }
  32. void xdebug_branch_info_free(xdebug_branch_info *branch_info)
  33. {
  34. unsigned int i;
  35. for (i = 0; i < branch_info->path_info.paths_count; i++) {
  36. free(branch_info->path_info.paths[i]->elements);
  37. free(branch_info->path_info.paths[i]);
  38. }
  39. free(branch_info->path_info.paths);
  40. xdebug_hash_destroy(branch_info->path_info.path_hash);
  41. free(branch_info->branches);
  42. xdebug_set_free(branch_info->entry_points);
  43. xdebug_set_free(branch_info->starts);
  44. xdebug_set_free(branch_info->ends);
  45. free(branch_info);
  46. }
  47. void xdebug_branch_info_update(xdebug_branch_info *branch_info, unsigned int pos, unsigned int lineno, unsigned int outidx, unsigned int jump_pos)
  48. {
  49. xdebug_set_add(branch_info->ends, pos);
  50. branch_info->branches[pos].out[outidx] = jump_pos;
  51. branch_info->branches[pos].start_lineno = lineno;
  52. }
  53. static void only_leave_first_catch(zend_op_array *opa, xdebug_branch_info *branch_info, int position)
  54. {
  55. unsigned int exit_jmp = opa->opcodes[position].extended_value;
  56. if (opa->opcodes[position].opcode == ZEND_FETCH_CLASS) {
  57. position++;
  58. }
  59. #if PHP_VERSION_ID >= 70100
  60. exit_jmp = position + ((signed int) opa->opcodes[position].extended_value / sizeof(zend_op));
  61. #else
  62. exit_jmp = opa->opcodes[position].extended_value;
  63. #endif
  64. if (opa->opcodes[position].opcode != ZEND_CATCH) {
  65. return;
  66. }
  67. if (opa->opcodes[exit_jmp].opcode == ZEND_FETCH_CLASS) {
  68. exit_jmp++;
  69. }
  70. if (opa->opcodes[exit_jmp].opcode == ZEND_CATCH) {
  71. only_leave_first_catch(opa, branch_info, exit_jmp);
  72. }
  73. xdebug_set_remove(branch_info->entry_points, position);
  74. }
  75. void xdebug_branch_post_process(zend_op_array *opa, xdebug_branch_info *branch_info)
  76. {
  77. unsigned int i;
  78. int in_branch = 0, last_start = -1;
  79. /* Figure out which CATCHes are chained, and hence which ones should be
  80. * considered entry points */
  81. for (i = 0; i < branch_info->entry_points->size; i++) {
  82. if (xdebug_set_in(branch_info->entry_points, i) && opa->opcodes[i].opcode == ZEND_CATCH) {
  83. #if PHP_VERSION_ID >= 70100
  84. only_leave_first_catch(opa, branch_info, i + ((signed int) opa->opcodes[i].extended_value / sizeof(zend_op)));
  85. #else
  86. only_leave_first_catch(opa, branch_info, opa->opcodes[i].extended_value);
  87. #endif
  88. }
  89. }
  90. for (i = 0; i < branch_info->starts->size; i++) {
  91. if (xdebug_set_in(branch_info->starts, i)) {
  92. if (in_branch) {
  93. branch_info->branches[last_start].out[0] = i;
  94. branch_info->branches[last_start].end_op = i-1;
  95. branch_info->branches[last_start].end_lineno = branch_info->branches[i].start_lineno;
  96. }
  97. last_start = i;
  98. in_branch = 1;
  99. }
  100. if (xdebug_set_in(branch_info->ends, i)) {
  101. branch_info->branches[last_start].out[0] = branch_info->branches[i].out[0];
  102. branch_info->branches[last_start].out[1] = branch_info->branches[i].out[1];
  103. branch_info->branches[last_start].end_op = i;
  104. branch_info->branches[last_start].end_lineno = branch_info->branches[i].start_lineno;
  105. in_branch = 0;
  106. }
  107. }
  108. }
  109. void xdebug_path_add(xdebug_path *path, unsigned int nr)
  110. {
  111. if (!path) {
  112. return;
  113. }
  114. if (path->elements_count == path->elements_size) {
  115. path->elements_size += 32;
  116. path->elements = realloc(path->elements, sizeof(unsigned int) * path->elements_size);
  117. }
  118. path->elements[path->elements_count] = nr;
  119. path->elements_count++;
  120. }
  121. static void xdebug_path_info_add_path(xdebug_path_info *path_info, xdebug_path *path)
  122. {
  123. if (path_info->paths_count == path_info->paths_size) {
  124. path_info->paths_size += 32;
  125. path_info->paths = realloc(path_info->paths, sizeof(xdebug_path*) * path_info->paths_size);
  126. }
  127. path_info->paths[path_info->paths_count] = path;
  128. path_info->paths_count++;
  129. }
  130. static void xdebug_path_info_make_sure_level_exists(xdebug_path_info *path_info, unsigned int level TSRMLS_DC)
  131. {
  132. unsigned int i = 0, orig_size;
  133. orig_size = path_info->paths_size;
  134. if (level >= path_info->paths_size) {
  135. path_info->paths_size = level + 32;
  136. path_info->paths = realloc(path_info->paths, sizeof(xdebug_path*) * path_info->paths_size);
  137. for (i = orig_size; i < XG(branches).size; i++) {
  138. XG(branches).last_branch_nr[i] = -1;
  139. }
  140. for (i = orig_size; i < path_info->paths_size; i++) {
  141. path_info->paths[i] = NULL;
  142. }
  143. }
  144. }
  145. void xdebug_path_info_add_path_for_level(xdebug_path_info *path_info, xdebug_path *path, unsigned int level TSRMLS_DC)
  146. {
  147. xdebug_path_info_make_sure_level_exists(path_info, level TSRMLS_CC);
  148. path_info->paths[level] = path;
  149. }
  150. xdebug_path *xdebug_path_info_get_path_for_level(xdebug_path_info *path_info, unsigned int level TSRMLS_DC)
  151. {
  152. xdebug_path_info_make_sure_level_exists(path_info, level TSRMLS_CC);
  153. return path_info->paths[level];
  154. }
  155. xdebug_path *xdebug_path_new(xdebug_path *old_path)
  156. {
  157. xdebug_path *tmp;
  158. tmp = calloc(1, sizeof(xdebug_path));
  159. if (old_path) {
  160. unsigned i;
  161. for (i = 0; i < old_path->elements_count; i++) {
  162. xdebug_path_add(tmp, old_path->elements[i]);
  163. }
  164. }
  165. return tmp;
  166. }
  167. void xdebug_path_free(xdebug_path *path)
  168. {
  169. if (path->elements) {
  170. free(path->elements);
  171. }
  172. free(path);
  173. }
  174. static unsigned int xdebug_branch_find_last_element(xdebug_path *path)
  175. {
  176. return path->elements[path->elements_count-1];
  177. }
  178. static int xdebug_path_exists(xdebug_path *path, unsigned int elem1, unsigned int elem2)
  179. {
  180. unsigned int i;
  181. for (i = 0; i < path->elements_count - 1; i++) {
  182. if (path->elements[i] == elem1 && path->elements[i + 1] == elem2) {
  183. return 1;
  184. }
  185. }
  186. return 0;
  187. }
  188. static void xdebug_branch_find_path(unsigned int nr, xdebug_branch_info *branch_info, xdebug_path *prev_path)
  189. {
  190. unsigned int out0, out1, last;
  191. xdebug_path *new_path;
  192. int found = 0;
  193. if (branch_info->path_info.paths_count > 4095) {
  194. return;
  195. }
  196. new_path = xdebug_path_new(prev_path);
  197. xdebug_path_add(new_path, nr);
  198. out0 = branch_info->branches[nr].out[0];
  199. out1 = branch_info->branches[nr].out[1];
  200. last = xdebug_branch_find_last_element(new_path);
  201. if (out0 != 0 && out0 != XDEBUG_JMP_EXIT && !xdebug_path_exists(new_path, last, out0)) {
  202. xdebug_branch_find_path(out0, branch_info, new_path);
  203. found = 1;
  204. }
  205. if (out1 != 0 && out1 != XDEBUG_JMP_EXIT && !xdebug_path_exists(new_path, last, out1)) {
  206. xdebug_branch_find_path(out1, branch_info, new_path);
  207. found = 1;
  208. }
  209. if (!found) {
  210. xdebug_path_info_add_path(&(branch_info->path_info), new_path);
  211. } else {
  212. xdebug_path_free(new_path);
  213. }
  214. }
  215. xdebug_path_info *xdebug_path_info_ctor(void)
  216. {
  217. xdebug_path_info *tmp;
  218. tmp = xdmalloc(sizeof(xdebug_path_info));
  219. tmp->paths_count = 0;
  220. tmp->paths_size = 0;
  221. tmp->paths = NULL;
  222. tmp->path_hash = NULL;
  223. return tmp;
  224. }
  225. void xdebug_path_info_dtor(xdebug_path_info *path_info)
  226. {
  227. unsigned int i;
  228. for (i = 0; i < path_info->paths_count; i++) {
  229. xdebug_path_free(path_info->paths[i]);
  230. }
  231. xdfree(path_info->paths);
  232. if (path_info->path_hash) {
  233. xdebug_hash_destroy(path_info->path_hash);
  234. }
  235. }
  236. void xdebug_create_key_for_path(xdebug_path *path, xdebug_str *str)
  237. {
  238. unsigned int i;
  239. char temp_nr[16];
  240. for (i = 0; i < path->elements_count; i++) {
  241. snprintf(temp_nr, 15, "%u:", path->elements[i]);
  242. xdebug_str_add(str, temp_nr, 0);
  243. }
  244. }
  245. void xdebug_branch_find_paths(xdebug_branch_info *branch_info)
  246. {
  247. unsigned int i;
  248. for (i = 0; i < branch_info->entry_points->size; i++) {
  249. if (xdebug_set_in(branch_info->entry_points, i)) {
  250. xdebug_branch_find_path(i, branch_info, NULL);
  251. }
  252. }
  253. branch_info->path_info.path_hash = xdebug_hash_alloc(128, NULL);
  254. for (i = 0; i < branch_info->path_info.paths_count; i++) {
  255. xdebug_str str = XDEBUG_STR_INITIALIZER;
  256. xdebug_create_key_for_path(branch_info->path_info.paths[i], &str);
  257. xdebug_hash_add(branch_info->path_info.path_hash, str.d, str.l, branch_info->path_info.paths[i]);
  258. xdfree(str.d);
  259. }
  260. }
  261. void xdebug_path_info_dump(xdebug_path *path TSRMLS_DC)
  262. {
  263. unsigned int i;
  264. for (i = 0; i < path->elements_count; i++) {
  265. printf("%d, ", path->elements[i]);
  266. }
  267. printf("\n");
  268. }
  269. void xdebug_branch_info_dump(zend_op_array *opa, xdebug_branch_info *branch_info TSRMLS_DC)
  270. {
  271. unsigned int i;
  272. for (i = 0; i < branch_info->starts->size; i++) {
  273. if (xdebug_set_in(branch_info->starts, i)) {
  274. printf("branch: #%3d; line: %5d-%5d; sop: %5d; eop: %5d",
  275. i,
  276. branch_info->branches[i].start_lineno,
  277. branch_info->branches[i].end_lineno,
  278. i,
  279. branch_info->branches[i].end_op
  280. );
  281. if (branch_info->branches[i].out[0]) {
  282. printf("; out1: %3d", branch_info->branches[i].out[0]);
  283. }
  284. if (branch_info->branches[i].out[1]) {
  285. printf("; out2: %3d", branch_info->branches[i].out[1]);
  286. }
  287. printf("\n");
  288. }
  289. }
  290. for (i = 0; i < branch_info->path_info.paths_count; i++) {
  291. printf("path #%d: ", i + 1);
  292. xdebug_path_info_dump(branch_info->path_info.paths[i] TSRMLS_CC);
  293. }
  294. }
  295. void xdebug_branch_info_mark_reached(char *filename, char *function_name, zend_op_array *op_array, long opcode_nr TSRMLS_DC)
  296. {
  297. xdebug_coverage_file *file;
  298. xdebug_coverage_function *function;
  299. xdebug_branch_info *branch_info;
  300. if (strcmp(XG(previous_mark_filename), filename) == 0) {
  301. file = XG(previous_mark_file);
  302. } else {
  303. if (!xdebug_hash_find(XG(code_coverage), filename, strlen(filename), (void *) &file)) {
  304. return;
  305. }
  306. XG(previous_mark_filename) = file->name;
  307. XG(previous_mark_file) = file;
  308. }
  309. /* If there is no branch info, we don't have to do more */
  310. if (!file->has_branch_info) {
  311. return;
  312. }
  313. /* Check if the function already exists in the hash */
  314. if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) {
  315. return;
  316. }
  317. branch_info = function->branch_info;
  318. if (opcode_nr != 0 && xdebug_set_in(branch_info->entry_points, opcode_nr)) {
  319. xdebug_code_coverage_end_of_function(op_array TSRMLS_CC);
  320. xdebug_code_coverage_start_of_function(op_array TSRMLS_CC);
  321. }
  322. if (xdebug_set_in(branch_info->starts, opcode_nr)) {
  323. char *key;
  324. void *dummy;
  325. /* Mark out for previous branch, if one is set */
  326. if (XG(branches).last_branch_nr[XG(level)] != -1) {
  327. if (branch_info->branches[XG(branches).last_branch_nr[XG(level)]].out[0] == opcode_nr) {
  328. branch_info->branches[XG(branches).last_branch_nr[XG(level)]].out_hit[0] = 1;
  329. }
  330. if (branch_info->branches[XG(branches).last_branch_nr[XG(level)]].out[1] == opcode_nr) {
  331. branch_info->branches[XG(branches).last_branch_nr[XG(level)]].out_hit[1] = 1;
  332. }
  333. }
  334. key = xdebug_sprintf("%d:%d:%d", opcode_nr, XG(branches).last_branch_nr[XG(level)], XG(function_count));
  335. if (!xdebug_hash_find(XG(visited_branches), key, strlen(key), (void*) &dummy)) {
  336. xdebug_path_add(XG(paths_stack)->paths[XG(level)], opcode_nr);
  337. xdebug_hash_add(XG(visited_branches), key, strlen(key), NULL);
  338. }
  339. xdfree(key);
  340. branch_info->branches[opcode_nr].hit = 1;
  341. XG(branches).last_branch_nr[XG(level)] = opcode_nr;
  342. }
  343. }
  344. void xdebug_branch_info_mark_end_of_function_reached(char *filename, char *function_name, char *key, int key_len TSRMLS_DC)
  345. {
  346. xdebug_coverage_file *file;
  347. xdebug_coverage_function *function;
  348. xdebug_branch_info *branch_info;
  349. xdebug_path *path;
  350. if (strcmp(XG(previous_mark_filename), filename) == 0) {
  351. file = XG(previous_mark_file);
  352. } else {
  353. if (!xdebug_hash_find(XG(code_coverage), filename, strlen(filename), (void *) &file)) {
  354. return;
  355. }
  356. XG(previous_mark_filename) = file->name;
  357. XG(previous_mark_file) = file;
  358. }
  359. /* If there is no branch info, we don't have to do more */
  360. if (!file->has_branch_info) {
  361. return;
  362. }
  363. /* Check if the function already exists in the hash */
  364. if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) {
  365. return;
  366. }
  367. branch_info = function->branch_info;
  368. if (!xdebug_hash_find(branch_info->path_info.path_hash, key, key_len, (void *) &path)) {
  369. return;
  370. }
  371. path->hit = 1;
  372. }
  373. void xdebug_branch_info_add_branches_and_paths(char *filename, char *function_name, xdebug_branch_info *branch_info TSRMLS_DC)
  374. {
  375. xdebug_coverage_file *file;
  376. xdebug_coverage_function *function;
  377. if (strcmp(XG(previous_filename), filename) == 0) {
  378. file = XG(previous_file);
  379. } else {
  380. /* Check if the file already exists in the hash */
  381. if (!xdebug_hash_find(XG(code_coverage), filename, strlen(filename), (void *) &file)) {
  382. /* The file does not exist, so we add it to the hash */
  383. file = xdebug_coverage_file_ctor(filename);
  384. xdebug_hash_add(XG(code_coverage), filename, strlen(filename), file);
  385. }
  386. XG(previous_filename) = file->name;
  387. XG(previous_file) = file;
  388. }
  389. /* Check if the function already exists in the hash */
  390. if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) {
  391. /* The file does not exist, so we add it to the hash */
  392. function = xdebug_coverage_function_ctor(function_name);
  393. xdebug_hash_add(file->functions, function_name, strlen(function_name), function);
  394. }
  395. if (branch_info) {
  396. file->has_branch_info = 1;
  397. }
  398. function->branch_info = branch_info;
  399. }