1 /*
2    +----------------------------------------------------------------------+
3    | Xdebug                                                               |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 2002-2021 Derick Rethans                               |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 1.01 of the Xdebug license,   |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available at through the world-wide-web at                           |
10    | https://xdebug.org/license.php                                       |
11    | If you did not receive a copy of the Xdebug license and are unable   |
12    | to obtain it through the world-wide-web, please send a note to       |
13    | derick@xdebug.org so we can mail you a copy immediately.             |
14    +----------------------------------------------------------------------+
15  */
16 
17 #include "php_xdebug.h"
18 #include "zend_extensions.h"
19 
20 #include "branch_info.h"
21 #include "code_coverage_private.h"
22 
23 #include "base/base.h"
24 #include "base/filter.h"
25 #include "lib/compat.h"
26 #include "lib/set.h"
27 #include "lib/var.h"
28 #include "tracing/tracing.h"
29 
30 /* True globals */
31 int zend_xdebug_filter_offset = -1;
32 int zend_xdebug_cc_run_offset = -1;
33 
34 extern ZEND_DECLARE_MODULE_GLOBALS(xdebug);
35 
xdebug_coverage_line_dtor(void * data)36 static void xdebug_coverage_line_dtor(void *data)
37 {
38 	xdebug_coverage_line *line = (xdebug_coverage_line *) data;
39 
40 	xdfree(line);
41 }
42 
xdebug_coverage_file_ctor(zend_string * filename)43 xdebug_coverage_file *xdebug_coverage_file_ctor(zend_string *filename)
44 {
45 	xdebug_coverage_file *file;
46 
47 	file = xdmalloc(sizeof(xdebug_coverage_file));
48 	file->name = zend_string_copy(filename);
49 	file->lines = xdebug_hash_alloc(128, xdebug_coverage_line_dtor);
50 	file->functions = xdebug_hash_alloc(128, xdebug_coverage_function_dtor);
51 	file->has_branch_info = 0;
52 
53 	return file;
54 }
55 
xdebug_coverage_file_dtor(void * data)56 static void xdebug_coverage_file_dtor(void *data)
57 {
58 	xdebug_coverage_file *file = (xdebug_coverage_file *) data;
59 
60 	xdebug_hash_destroy(file->lines);
61 	xdebug_hash_destroy(file->functions);
62 	zend_string_release(file->name);
63 	xdfree(file);
64 }
65 
xdebug_coverage_function_ctor(char * function_name)66 xdebug_coverage_function *xdebug_coverage_function_ctor(char *function_name)
67 {
68 	xdebug_coverage_function *function;
69 
70 	function = xdmalloc(sizeof(xdebug_coverage_function));
71 	function->name = xdstrdup(function_name);
72 	function->branch_info = NULL;
73 
74 	return function;
75 }
76 
xdebug_coverage_function_dtor(void * data)77 void xdebug_coverage_function_dtor(void *data)
78 {
79 	xdebug_coverage_function *function = (xdebug_coverage_function *) data;
80 
81 	if (function->branch_info) {
82 		xdebug_branch_info_free(function->branch_info);
83 	}
84 	xdfree(function->name);
85 	xdfree(function);
86 }
87 
xdebug_func_format(char * buffer,size_t buffer_size,xdebug_func * func)88 static void xdebug_func_format(char *buffer, size_t buffer_size, xdebug_func *func)
89 {
90 	if (func->type == XFUNC_NORMAL) {
91 		int len = strlen(func->function);
92 
93 		if (len + 1 > buffer_size) {
94 			goto error;
95 		}
96 		memcpy(buffer, func->function, len);
97 		buffer[len] = '\0';
98 		return;
99 	}
100 
101 	if (func->type == XFUNC_MEMBER) {
102 		int func_len = strlen(func->function);
103 		int len = ZSTR_LEN(func->object_class) + 2 + func_len;
104 
105 		if (len + 1 > buffer_size) {
106 			goto error;
107 		}
108 		memcpy(buffer, ZSTR_VAL(func->object_class), ZSTR_LEN(func->object_class));
109 		memcpy(buffer + ZSTR_LEN(func->object_class), "->", 2);
110 		memcpy(buffer + ZSTR_LEN(func->object_class) + 2, func->function, func_len);
111 		buffer[len] = '\0';
112 		return;
113 	}
114 
115 error:
116 	memcpy(buffer, "?", 1);
117 	buffer[1] = '\0';
118 }
119 
xdebug_build_fname_from_oparray(xdebug_func * tmp,zend_op_array * opa)120 static void xdebug_build_fname_from_oparray(xdebug_func *tmp, zend_op_array *opa)
121 {
122 	int wrapped = 0;
123 
124 	memset(tmp, 0, sizeof(xdebug_func));
125 
126 	if (opa->function_name) {
127 		if (opa->fn_flags & ZEND_ACC_CLOSURE) {
128 			tmp->function = xdebug_wrap_closure_location_around_function_name(opa, STR_NAME_VAL(opa->function_name));
129 			wrapped = 1;
130 #if PHP_VERSION_ID >= 70400
131 		} else if (
132 			opa->fn_flags & ZEND_ACC_TRAIT_CLONE
133 			|| (opa->scope && opa->scope->ce_flags & ZEND_ACC_TRAIT)
134 		) {
135 			tmp->function = xdebug_wrap_location_around_function_name("trait-method", opa, STR_NAME_VAL(opa->function_name));
136 			wrapped = 1;
137 #endif
138 		} else {
139 			tmp->function = xdstrdup(STR_NAME_VAL(opa->function_name));
140 		}
141 	} else {
142 		tmp->function = xdstrdup("{main}");
143 		tmp->type = XFUNC_MAIN;
144 	}
145 
146 	if (opa->scope && !wrapped) {
147 		tmp->type = XFUNC_MEMBER;
148 		tmp->object_class = zend_string_copy(opa->scope->name);
149 	} else {
150 		tmp->type = XFUNC_NORMAL;
151 	}
152 }
153 
xdebug_print_opcode_info(zend_execute_data * execute_data,const zend_op * cur_opcode)154 static void xdebug_print_opcode_info(zend_execute_data *execute_data, const zend_op *cur_opcode)
155 {
156 	zend_op_array *op_array = &execute_data->func->op_array;
157 	xdebug_func func_info;
158 	char function_name[1024];
159 	long opnr = execute_data->opline - execute_data->func->op_array.opcodes;
160 
161 	xdebug_build_fname_from_oparray(&func_info, op_array);
162 	xdebug_func_format(function_name, sizeof(function_name), &func_info);
163 	if (func_info.object_class) {
164 		zend_string_release(func_info.object_class);
165 	}
166 	if (func_info.scope_class) {
167 		zend_string_release(func_info.scope_class);
168 	}
169 	if (func_info.function) {
170 		xdfree(func_info.function);
171 	}
172 
173 	xdebug_branch_info_mark_reached(op_array->filename, function_name, op_array, opnr);
174 }
175 
xdebug_check_branch_entry_handler(XDEBUG_OPCODE_HANDLER_ARGS)176 static int xdebug_check_branch_entry_handler(XDEBUG_OPCODE_HANDLER_ARGS)
177 {
178 	zend_op_array *op_array = &execute_data->func->op_array;
179 	const zend_op *cur_opcode = execute_data->opline;
180 
181 	if (!op_array->reserved[XG_COV(code_coverage_filter_offset)] && XG_COV(code_coverage_active)) {
182 		xdebug_print_opcode_info(execute_data, cur_opcode);
183 	}
184 
185 	return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU);
186 }
187 
xdebug_count_line(zend_string * filename,int lineno,int executable,int deadcode)188 static void xdebug_count_line(zend_string *filename, int lineno, int executable, int deadcode)
189 {
190 	xdebug_coverage_file *file;
191 	xdebug_coverage_line *line;
192 
193 	if (XG_COV(previous_filename) && zend_string_equals(XG_COV(previous_filename), filename)) {
194 		file = XG_COV(previous_file);
195 	} else {
196 		/* Check if the file already exists in the hash */
197 		if (!xdebug_hash_find(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &file)) {
198 			/* The file does not exist, so we add it to the hash */
199 			file = xdebug_coverage_file_ctor(filename);
200 
201 			xdebug_hash_add(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), file);
202 		}
203 		if (XG_COV(previous_filename)) {
204 			zend_string_release(XG_COV(previous_filename));
205 		}
206 		XG_COV(previous_filename) = zend_string_copy(file->name);
207 		XG_COV(previous_file) = file;
208 	}
209 
210 	/* Check if the line already exists in the hash */
211 	if (!xdebug_hash_index_find(file->lines, lineno, (void *) &line)) {
212 		line = xdmalloc(sizeof(xdebug_coverage_line));
213 		line->lineno = lineno;
214 		line->count = 0;
215 		line->executable = 0;
216 
217 		xdebug_hash_index_add(file->lines, lineno, line);
218 	}
219 
220 	if (executable) {
221 		if (line->executable != 1 && deadcode) {
222 			line->executable = 2;
223 		} else {
224 			line->executable = 1;
225 		}
226 	} else {
227 		line->count++;
228 	}
229 }
230 
xdebug_common_override_handler(XDEBUG_OPCODE_HANDLER_ARGS)231 static int xdebug_common_override_handler(XDEBUG_OPCODE_HANDLER_ARGS)
232 {
233 	zend_op_array *op_array = &execute_data->func->op_array;
234 	const zend_op *cur_opcode = execute_data->opline;
235 
236 	if (!op_array->reserved[XG_COV(code_coverage_filter_offset)] && XG_COV(code_coverage_active)) {
237 		int      lineno;
238 
239 		lineno = cur_opcode->lineno;
240 
241 		xdebug_print_opcode_info(execute_data, cur_opcode);
242 		xdebug_count_line(op_array->filename, lineno, 0, 0);
243 	}
244 
245 	return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU);
246 }
247 
xdebug_coverage_include_or_eval_handler(XDEBUG_OPCODE_HANDLER_ARGS)248 static int xdebug_coverage_include_or_eval_handler(XDEBUG_OPCODE_HANDLER_ARGS)
249 {
250 	zend_op_array *op_array = &execute_data->func->op_array;
251 	const zend_op *opline = execute_data->opline;
252 
253 	xdebug_coverage_record_if_active(execute_data, op_array);
254 
255 	return xdebug_call_original_opcode_handler_if_set(opline->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU);
256 }
257 
prefill_from_opcode(zend_string * filename,zend_op opcode,int deadcode)258 static void prefill_from_opcode(zend_string *filename, zend_op opcode, int deadcode)
259 {
260 	if (
261 		opcode.opcode != ZEND_NOP &&
262 		opcode.opcode != ZEND_EXT_NOP &&
263 		opcode.opcode != ZEND_RECV &&
264 		opcode.opcode != ZEND_RECV_INIT
265 #if PHP_VERSION_ID < 70400
266 		&& opcode.opcode != ZEND_VERIFY_ABSTRACT_CLASS
267 		&& opcode.opcode != ZEND_ADD_INTERFACE
268 #endif
269 		&& opcode.opcode != ZEND_OP_DATA
270 		&& opcode.opcode != ZEND_TICKS
271 		&& opcode.opcode != ZEND_FAST_CALL
272 		&& opcode.opcode != ZEND_RECV_VARIADIC
273 	) {
274 		xdebug_count_line(filename, opcode.lineno, 1, deadcode);
275 	}
276 }
277 
278 #define XDEBUG_ZNODE_ELEM(node,var) node.var
279 
xdebug_find_jumps(zend_op_array * opa,unsigned int position,size_t * jump_count,int * jumps)280 static int xdebug_find_jumps(zend_op_array *opa, unsigned int position, size_t *jump_count, int *jumps)
281 {
282 #if ZEND_USE_ABS_JMP_ADDR
283 	zend_op *base_address = &(opa->opcodes[0]);
284 #endif
285 
286 	zend_op opcode = opa->opcodes[position];
287 	if (opcode.opcode == ZEND_JMP) {
288 		jumps[0] = XDEBUG_ZNODE_JMP_LINE(opcode.op1, position, base_address);
289 		*jump_count = 1;
290 		return 1;
291 
292 	} else if (
293 		opcode.opcode == ZEND_JMPZ ||
294 		opcode.opcode == ZEND_JMPNZ ||
295 		opcode.opcode == ZEND_JMPZ_EX ||
296 		opcode.opcode == ZEND_JMPNZ_EX
297 	) {
298 		jumps[0] = position + 1;
299 		jumps[1] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address);
300 		*jump_count = 2;
301 		return 1;
302 
303 	} else if (opcode.opcode == ZEND_JMPZNZ) {
304 		jumps[0] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address);
305 		jumps[1] = position + ((int32_t) opcode.extended_value / (int32_t) sizeof(zend_op));
306 		*jump_count = 2;
307 		return 1;
308 
309 	} else if (opcode.opcode == ZEND_FE_FETCH_R || opcode.opcode == ZEND_FE_FETCH_RW) {
310 		jumps[0] = position + 1;
311 		jumps[1] = position + (opcode.extended_value / sizeof(zend_op));
312 		*jump_count = 2;
313 		return 1;
314 
315 	} else if (opcode.opcode == ZEND_FE_RESET_R || opcode.opcode == ZEND_FE_RESET_RW) {
316 		jumps[0] = position + 1;
317 		jumps[1] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address);
318 		*jump_count = 2;
319 		return 1;
320 
321 	} else if (opcode.opcode == ZEND_CATCH) {
322 		*jump_count = 2;
323 		jumps[0] = position + 1;
324 #if PHP_VERSION_ID >= 70300
325 		if (!(opcode.extended_value & ZEND_LAST_CATCH)) {
326 			jumps[1] = XDEBUG_ZNODE_JMP_LINE(opcode.op2, position, base_address);
327 #else
328 		if (!opcode.result.num) {
329 			jumps[1] = position + (opcode.extended_value / sizeof(zend_op));
330 #endif
331 			if (jumps[1] == jumps[0]) {
332 				jumps[1] = XDEBUG_JMP_NOT_SET;
333 				*jump_count = 1;
334 			}
335 		} else {
336 			jumps[1] = XDEBUG_JMP_EXIT;
337 		}
338 		return 1;
339 
340 	} else if (opcode.opcode == ZEND_GOTO) {
341 		jumps[0] = XDEBUG_ZNODE_JMP_LINE(opcode.op1, position, base_address);
342 		*jump_count = 1;
343 		return 1;
344 
345 	} else if (opcode.opcode == ZEND_FAST_CALL) {
346 		jumps[0] = XDEBUG_ZNODE_JMP_LINE(opcode.op1, position, base_address);
347 		jumps[1] = position + 1;
348 		*jump_count = 2;
349 		return 1;
350 
351 	} else if (opcode.opcode == ZEND_FAST_RET) {
352 		jumps[0] = XDEBUG_JMP_EXIT;
353 		*jump_count = 1;
354 		return 1;
355 
356 	} else if (
357 		opcode.opcode == ZEND_GENERATOR_RETURN ||
358 		opcode.opcode == ZEND_EXIT ||
359 		opcode.opcode == ZEND_THROW ||
360 #if PHP_VERSION_ID >= 80000
361 		opcode.opcode == ZEND_MATCH_ERROR ||
362 #endif
363 		opcode.opcode == ZEND_RETURN
364 	) {
365 		jumps[0] = XDEBUG_JMP_EXIT;
366 		*jump_count = 1;
367 		return 1;
368 	} else if (
369 # if PHP_VERSION_ID >= 80000
370 		opcode.opcode == ZEND_MATCH ||
371 # endif
372 		opcode.opcode == ZEND_SWITCH_LONG ||
373 		opcode.opcode == ZEND_SWITCH_STRING
374 	) {
375 		zval *array_value;
376 		HashTable *myht;
377 		zval *val;
378 
379 # if PHP_VERSION_ID >= 70300
380 		array_value = RT_CONSTANT(&opa->opcodes[position], opcode.op2);
381 # else
382 		array_value = RT_CONSTANT_EX(opa->literals, opcode.op2);
383 # endif
384 		myht = Z_ARRVAL_P(array_value);
385 
386 		/* All 'case' statements */
387 		ZEND_HASH_FOREACH_VAL_IND(myht, val) {
388 			if (*jump_count < XDEBUG_BRANCH_MAX_OUTS - 2) {
389 				jumps[*jump_count] = position + (val->value.lval / sizeof(zend_op));
390 				(*jump_count)++;
391 			}
392 		} ZEND_HASH_FOREACH_END();
393 
394 		/* The 'default' case */
395 		jumps[*jump_count] = position + (opcode.extended_value / sizeof(zend_op));
396 		(*jump_count)++;
397 
398 # if PHP_VERSION_ID >= 80000
399 		if (opcode.opcode != ZEND_MATCH) {
400 # endif
401 			/* The 'next' opcode */
402 			jumps[*jump_count] = position + 1;
403 			(*jump_count)++;
404 # if PHP_VERSION_ID >= 80000
405 		}
406 # endif
407 
408 		return 1;
409 	}
410 
411 	return 0;
412 }
413 
414 static void xdebug_analyse_branch(zend_op_array *opa, unsigned int position, xdebug_set *set, xdebug_branch_info *branch_info)
415 {
416 	/* fprintf(stderr, "Branch analysis from position: %d\n", position); */
417 
418 	if (branch_info) {
419 		xdebug_set_add(branch_info->starts, position);
420 		branch_info->branches[position].start_lineno = opa->opcodes[position].lineno;
421 	}
422 
423 	/* First we see if the branch has been visited, if so we bail out. */
424 	if (xdebug_set_in(set, position)) {
425 		return;
426 	}
427 
428 	/* fprintf(stderr, "XDEBUG Adding %d\n", position); */
429 	/* Loop over the opcodes until the end of the array, or until a jump point has been found */
430 	xdebug_set_add(set, position);
431 	while (position < opa->last) {
432 		size_t jump_count = 0;
433 		int    jumps[XDEBUG_BRANCH_MAX_OUTS];
434 		size_t i;
435 
436 		/* See if we have a jump instruction */
437 		if (xdebug_find_jumps(opa, position, &jump_count, jumps)) {
438 			for (i = 0; i < jump_count; i++) {
439 				if (jumps[i] == XDEBUG_JMP_EXIT || jumps[i] != XDEBUG_JMP_NOT_SET) {
440 					if (branch_info) {
441 						xdebug_branch_info_update(branch_info, position, opa->opcodes[position].lineno, i, jumps[i]);
442 					}
443 					if (jumps[i] != XDEBUG_JMP_EXIT) {
444 						xdebug_analyse_branch(opa, jumps[i], set, branch_info);
445 					}
446 				}
447 			}
448 			break;
449 		}
450 
451 		/* See if we have a throw instruction */
452 		if (opa->opcodes[position].opcode == ZEND_THROW) {
453 			/* fprintf(stderr, "Throw found at %d\n", position); */
454 			if (branch_info) {
455 				xdebug_set_add(branch_info->ends, position);
456 				branch_info->branches[position].start_lineno = opa->opcodes[position].lineno;
457 			}
458 			break;
459 		}
460 
461 		/* See if we have an exit instruction */
462 		if (opa->opcodes[position].opcode == ZEND_EXIT) {
463 			/* fprintf(stderr, "X* Return found\n"); */
464 			if (branch_info) {
465 				xdebug_set_add(branch_info->ends, position);
466 				branch_info->branches[position].start_lineno = opa->opcodes[position].lineno;
467 			}
468 			break;
469 		}
470 		/* See if we have a return instruction */
471 		if (
472 			opa->opcodes[position].opcode == ZEND_RETURN
473 			|| opa->opcodes[position].opcode == ZEND_RETURN_BY_REF
474 		) {
475 			/*(fprintf(stderr, "XDEBUG Return found\n");)*/
476 			if (branch_info) {
477 				xdebug_set_add(branch_info->ends, position);
478 				branch_info->branches[position].start_lineno = opa->opcodes[position].lineno;
479 			}
480 			break;
481 		}
482 
483 		position++;
484 		/*(fprintf(stderr, "XDEBUG Adding %d\n", position);)*/
485 		xdebug_set_add(set, position);
486 	}
487 }
488 
489 static void xdebug_analyse_oparray(zend_op_array *opa, xdebug_set *set, xdebug_branch_info *branch_info)
490 {
491 	unsigned int position = 0;
492 
493 	while (position < opa->last) {
494 		if (position == 0) {
495 			xdebug_analyse_branch(opa, position, set, branch_info);
496 			if (branch_info) {
497 				xdebug_set_add(branch_info->entry_points, position);
498 			}
499 		} else if (opa->opcodes[position].opcode == ZEND_CATCH) {
500 			xdebug_analyse_branch(opa, position, set, branch_info);
501 			if (branch_info) {
502 				xdebug_set_add(branch_info->entry_points, position);
503 			}
504 		}
505 		position++;
506 	}
507 	if (branch_info) {
508 		xdebug_set_add(branch_info->ends, opa->last-1);
509 		branch_info->branches[opa->last-1].start_lineno = opa->opcodes[opa->last-1].lineno;
510 	}
511 }
512 
513 static void prefill_from_oparray(zend_string *filename, zend_op_array *op_array)
514 {
515 	unsigned int i;
516 	xdebug_set *set = NULL;
517 	xdebug_branch_info *branch_info = NULL;
518 
519 	op_array->reserved[XG_COV(dead_code_analysis_tracker_offset)] = (void*) XG_COV(dead_code_last_start_id);
520 
521 	/* Check for abstract methods and simply return from this function in those
522 	 * cases. */
523 	if (op_array->fn_flags & ZEND_ACC_ABSTRACT) {
524 		return;
525 	}
526 
527 	/* Check whether this function should be filtered out */
528 	{
529 /*
530 		function_stack_entry tmp_fse;
531 		tmp_fse.filename = STR_NAME_VAL(op_array->filename);
532 		xdebug_build_fname_from_oparray(&tmp_fse.function, op_array);
533 		printf("    - PREFIL FILTERED FOR %s (%s::%s): %s\n",
534 			tmp_fse.filename, tmp_fse.function.class, tmp_fse.function.function,
535 			op_array->reserved[XG_COV(code_coverage_filter_offset)] ? "YES" : "NO");
536 */
537 		if (op_array->reserved[XG_COV(code_coverage_filter_offset)]) {
538 			return;
539 		}
540 	}
541 
542 	/* Run dead code analysis if requested */
543 	if (XG_COV(code_coverage_dead_code_analysis) && (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) {
544 		set = xdebug_set_create(op_array->last);
545 		if (XG_COV(code_coverage_branch_check)) {
546 			branch_info = xdebug_branch_info_create(op_array->last);
547 		}
548 
549 		xdebug_analyse_oparray(op_array, set, branch_info);
550 	}
551 
552 	/* The normal loop then finally */
553 	for (i = 0; i < op_array->last; i++) {
554 		zend_op opcode = op_array->opcodes[i];
555 		prefill_from_opcode(filename, opcode, set ? !xdebug_set_in(set, i) : 0);
556 	}
557 
558 	if (set) {
559 		xdebug_set_free(set);
560 	}
561 	if (branch_info) {
562 		char function_name[1024];
563 		xdebug_func func_info;
564 
565 		xdebug_build_fname_from_oparray(&func_info, op_array);
566 		xdebug_func_format(function_name, sizeof(function_name), &func_info);
567 
568 		if (func_info.object_class) {
569 			zend_string_release(func_info.object_class);
570 		}
571 		if (func_info.scope_class) {
572 			zend_string_release(func_info.scope_class);
573 		}
574 		if (func_info.function) {
575 			xdfree(func_info.function);
576 		}
577 
578 		xdebug_branch_post_process(op_array, branch_info);
579 		xdebug_branch_find_paths(branch_info);
580 		xdebug_branch_info_add_branches_and_paths(filename, (char*) function_name, branch_info);
581 	}
582 
583 #if PHP_VERSION_ID >= 80100
584 	if (!op_array->num_dynamic_func_defs) {
585 		return;
586 	}
587 
588 	for (i = 0; i < op_array->num_dynamic_func_defs; i++) {
589 		prefill_from_oparray(filename, op_array->dynamic_func_defs[i]);
590 	}
591 #endif
592 }
593 
594 static int prefill_from_function_table(zend_op_array *opa)
595 {
596 	if (opa->type == ZEND_USER_FUNCTION) {
597 		if ((long) opa->reserved[XG_COV(dead_code_analysis_tracker_offset)] < XG_COV(dead_code_last_start_id)) {
598 			prefill_from_oparray(opa->filename, opa);
599 		}
600 	}
601 
602 	return ZEND_HASH_APPLY_KEEP;
603 }
604 
605 /* Set correct int format to use */
606 #if SIZEOF_ZEND_LONG == 4
607 # define XDEBUG_PTR_KEY_LEN 8
608 # define XDEBUG_PTR_KEY_FMT "%08X"
609 #else
610 # define XDEBUG_PTR_KEY_LEN 16
611 # define XDEBUG_PTR_KEY_FMT "%016lX"
612 #endif
613 
614 static int prefill_from_class_table(zend_class_entry *ce)
615 {
616 	if (ce->type == ZEND_USER_CLASS) {
617 		zend_op_array *val;
618 
619 		ZEND_HASH_FOREACH_PTR(&ce->function_table, val) {
620 			prefill_from_function_table(val);
621 		} ZEND_HASH_FOREACH_END();
622 	}
623 
624 	return ZEND_HASH_APPLY_KEEP;
625 }
626 
627 static void xdebug_prefill_code_coverage(zend_op_array *op_array)
628 {
629 	zend_op_array    *function_op_array;
630 	zend_class_entry *class_entry;
631 
632 	if ((long) op_array->reserved[XG_COV(dead_code_analysis_tracker_offset)] < XG_COV(dead_code_last_start_id)) {
633 		prefill_from_oparray(op_array->filename, op_array);
634 	}
635 
636 	ZEND_HASH_REVERSE_FOREACH_PTR(CG(function_table), function_op_array) {
637 		if (_idx == XG_COV(prefill_function_count)) {
638 			break;
639 		}
640 		prefill_from_function_table(function_op_array);
641 	} ZEND_HASH_FOREACH_END();
642 	XG_COV(prefill_function_count) = CG(function_table)->nNumUsed;
643 
644 	ZEND_HASH_REVERSE_FOREACH_PTR(CG(class_table), class_entry) {
645 		if (_idx == XG_COV(prefill_class_count)) {
646 			break;
647 		}
648 		prefill_from_class_table(class_entry);
649 	} ZEND_HASH_FOREACH_END();
650 	XG_COV(prefill_class_count) = CG(class_table)->nNumUsed;
651 }
652 
653 void xdebug_code_coverage_start_of_function(zend_op_array *op_array, char *function_name)
654 {
655 	xdebug_path *path = xdebug_path_new(NULL);
656 
657 	xdebug_prefill_code_coverage(op_array);
658 	xdebug_path_info_add_path_for_level(XG_COV(paths_stack), path, XDEBUG_VECTOR_COUNT(XG_BASE(stack)));
659 
660 	if (XG_COV(branches).size == 0 || XDEBUG_VECTOR_COUNT(XG_BASE(stack)) >= XG_COV(branches).size) {
661 		XG_COV(branches).size = XDEBUG_VECTOR_COUNT(XG_BASE(stack)) + 32;
662 		XG_COV(branches).last_branch_nr = realloc(XG_COV(branches).last_branch_nr, sizeof(int) * XG_COV(branches.size));
663 	}
664 
665 	XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))] = -1;
666 }
667 
668 void xdebug_code_coverage_end_of_function(zend_op_array *op_array, zend_string *filename, char *function_name)
669 {
670 	xdebug_str str = XDEBUG_STR_INITIALIZER;
671 	xdebug_path *path = xdebug_path_info_get_path_for_level(XG_COV(paths_stack), XDEBUG_VECTOR_COUNT(XG_BASE(stack)));
672 
673 	if (!path || !path->elements) {
674 		return;
675 	}
676 
677 	xdebug_create_key_for_path(path, &str);
678 
679 	xdebug_branch_info_mark_end_of_function_reached(filename, function_name, str.d, str.l);
680 
681 	xdfree(str.d);
682 
683 	if (path) {
684 		xdebug_path_free(path);
685 	}
686 }
687 
688 PHP_FUNCTION(xdebug_start_code_coverage)
689 {
690 	zend_long options = 0;
691 
692 	if (!XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) {
693 		php_error(E_WARNING, "Code coverage needs to be enabled in php.ini by setting 'xdebug.mode' to 'coverage'");
694 		RETURN_FALSE;
695 	}
696 
697 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &options) == FAILURE) {
698 		return;
699 	}
700 
701 	XG_COV(code_coverage_unused) = (options & XDEBUG_CC_OPTION_UNUSED);
702 	XG_COV(code_coverage_dead_code_analysis) = (options & XDEBUG_CC_OPTION_DEAD_CODE);
703 	XG_COV(code_coverage_branch_check) = (options & XDEBUG_CC_OPTION_BRANCH_CHECK);
704 
705 	XG_COV(code_coverage_active) = 1;
706 	RETURN_TRUE;
707 }
708 
709 PHP_FUNCTION(xdebug_stop_code_coverage)
710 {
711 	zend_bool cleanup = 1;
712 
713 	if (!XDEBUG_MODE_IS(XDEBUG_MODE_COVERAGE)) {
714 		RETURN_FALSE;
715 	}
716 
717 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &cleanup) == FAILURE) {
718 		return;
719 	}
720 	if (!XG_COV(code_coverage_active)) {
721 		RETURN_FALSE;
722 	}
723 
724 	if (cleanup) {
725 		if (XG_COV(previous_filename)) {
726 			zend_string_release(XG_COV(previous_filename));
727 		}
728 		XG_COV(previous_filename) = NULL;
729 		XG_COV(previous_file) = NULL;
730 		if (XG_COV(previous_mark_filename)) {
731 			zend_string_release(XG_COV(previous_mark_filename));
732 		}
733 		XG_COV(previous_mark_filename) = NULL;
734 		XG_COV(previous_mark_file) = NULL;
735 		xdebug_hash_destroy(XG_COV(code_coverage_info));
736 		XG_COV(code_coverage_info) = xdebug_hash_alloc(32, xdebug_coverage_file_dtor);
737 		XG_COV(dead_code_last_start_id)++;
738 		xdebug_path_info_dtor(XG_COV(paths_stack));
739 		XG_COV(paths_stack) = xdebug_path_info_ctor();
740 	}
741 
742 	XG_COV(code_coverage_active) = 0;
743 
744 	RETURN_TRUE;
745 }
746 
747 #if PHP_VERSION_ID >= 80000
748 static int xdebug_lineno_cmp(Bucket *f, Bucket *s)
749 {
750 #else
751 static int xdebug_lineno_cmp(const void *a, const void *b)
752 {
753 	Bucket *f = (Bucket *) a;
754 	Bucket *s = (Bucket *) b;
755 #endif
756 	if (f->h < s->h) {
757 		return -1;
758 	} else if (f->h > s->h) {
759 		return 1;
760 	} else {
761 		return 0;
762 	}
763 }
764 
765 
766 static void add_line(void *ret, xdebug_hash_element *e)
767 {
768 	xdebug_coverage_line *line = (xdebug_coverage_line*) e->ptr;
769 	zval                 *retval = (zval*) ret;
770 
771 	if (line->executable && (line->count == 0)) {
772 		add_index_long(retval, line->lineno, -line->executable);
773 	} else {
774 		add_index_long(retval, line->lineno, 1);
775 	}
776 }
777 
778 static void add_branches(zval *retval, xdebug_branch_info *branch_info)
779 {
780 	zval *branches, *branch, *out, *out_hit;
781 	unsigned int i;
782 
783 	XDEBUG_MAKE_STD_ZVAL(branches);
784 	array_init(branches);
785 
786 	for (i = 0; i < branch_info->starts->size; i++) {
787 		if (xdebug_set_in(branch_info->starts, i)) {
788 			size_t j = 0;
789 
790 			XDEBUG_MAKE_STD_ZVAL(branch);
791 			array_init(branch);
792 			add_assoc_long(branch, "op_start", i);
793 			add_assoc_long(branch, "op_end", branch_info->branches[i].end_op);
794 			add_assoc_long(branch, "line_start", branch_info->branches[i].start_lineno);
795 			add_assoc_long(branch, "line_end", branch_info->branches[i].end_lineno);
796 
797 			add_assoc_long(branch, "hit", branch_info->branches[i].hit);
798 
799 			XDEBUG_MAKE_STD_ZVAL(out);
800 			array_init(out);
801 			for (j = 0; j < branch_info->branches[i].outs_count; j++) {
802 				if (branch_info->branches[i].outs[j]) {
803 					add_index_long(out, j, branch_info->branches[i].outs[j]);
804 				}
805 			}
806 			add_assoc_zval(branch, "out", out);
807 
808 			XDEBUG_MAKE_STD_ZVAL(out_hit);
809 			array_init(out_hit);
810 			for (j = 0; j < branch_info->branches[i].outs_count; j++) {
811 				if (branch_info->branches[i].outs[j]) {
812 					add_index_long(out_hit, j, branch_info->branches[i].outs_hit[j]);
813 				}
814 			}
815 			add_assoc_zval(branch, "out_hit", out_hit);
816 
817 			add_index_zval(branches, i, branch);
818 			efree(out_hit);
819 			efree(out);
820 			efree(branch);
821 		}
822 	}
823 
824 	add_assoc_zval_ex(retval, "branches", HASH_KEY_SIZEOF("branches"), branches);
825 
826 	efree(branches);
827 }
828 
829 static void add_paths(zval *retval, xdebug_branch_info *branch_info)
830 {
831 	zval *paths, *path, *path_container;
832 	unsigned int i, j;
833 
834 	XDEBUG_MAKE_STD_ZVAL(paths);
835 	array_init(paths);
836 
837 	for (i = 0; i < branch_info->path_info.paths_count; i++) {
838 		XDEBUG_MAKE_STD_ZVAL(path);
839 		array_init(path);
840 
841 		XDEBUG_MAKE_STD_ZVAL(path_container);
842 		array_init(path_container);
843 
844 		for (j = 0; j < branch_info->path_info.paths[i]->elements_count; j++) {
845 			add_next_index_long(path, branch_info->path_info.paths[i]->elements[j]);
846 		}
847 
848 		add_assoc_zval(path_container, "path", path);
849 		add_assoc_long(path_container, "hit", branch_info->path_info.paths[i]->hit);
850 
851 		add_next_index_zval(paths, path_container);
852 
853 		efree(path_container);
854 		efree(path);
855 	}
856 
857 	add_assoc_zval_ex(retval, "paths", HASH_KEY_SIZEOF("paths"), paths);
858 
859 	efree(paths);
860 }
861 
862 static void add_cc_function(void *ret, xdebug_hash_element *e)
863 {
864 	xdebug_coverage_function *function = (xdebug_coverage_function*) e->ptr;
865 	zval                     *retval = (zval*) ret;
866 	zval                     *function_info;
867 
868 	XDEBUG_MAKE_STD_ZVAL(function_info);
869 	array_init(function_info);
870 
871 	if (function->branch_info) {
872 		add_branches(function_info, function->branch_info);
873 		add_paths(function_info, function->branch_info);
874 	}
875 
876 	add_assoc_zval_ex(retval, function->name, HASH_KEY_STRLEN(function->name), function_info);
877 
878 	efree(function_info);
879 }
880 
881 static void add_file(void *ret, xdebug_hash_element *e)
882 {
883 	xdebug_coverage_file *file = (xdebug_coverage_file*) e->ptr;
884 	zval                 *retval = (zval*) ret;
885 	zval                 *lines, *functions, *file_info;
886 	HashTable            *target_hash;
887 
888 	/* Add all the lines */
889 	XDEBUG_MAKE_STD_ZVAL(lines);
890 	array_init(lines);
891 
892 	xdebug_hash_apply(file->lines, (void *) lines, add_line);
893 
894 	/* Sort on linenumber */
895 	target_hash = HASH_OF(lines);
896 	zend_hash_sort(target_hash, xdebug_lineno_cmp, 0);
897 
898 	/* Add the branch and path info */
899 	if (XG_COV(code_coverage_branch_check)) {
900 		XDEBUG_MAKE_STD_ZVAL(file_info);
901 		array_init(file_info);
902 
903 		XDEBUG_MAKE_STD_ZVAL(functions);
904 		array_init(functions);
905 
906 		xdebug_hash_apply(file->functions, (void *) functions, add_cc_function);
907 
908 		add_assoc_zval_ex(file_info, "lines", HASH_KEY_SIZEOF("lines"), lines);
909 		add_assoc_zval_ex(file_info, "functions", HASH_KEY_SIZEOF("functions"), functions);
910 
911 		add_assoc_zval_ex(retval, ZSTR_VAL(file->name), ZSTR_LEN(file->name), file_info);
912 		efree(functions);
913 		efree(file_info);
914 	} else {
915 		add_assoc_zval_ex(retval, ZSTR_VAL(file->name), ZSTR_LEN(file->name), lines);
916 	}
917 
918 	efree(lines);
919 }
920 
921 PHP_FUNCTION(xdebug_get_code_coverage)
922 {
923 	array_init(return_value);
924 
925 	if (!XG_COV(code_coverage_info)) {
926 		return;
927 	}
928 
929 	xdebug_hash_apply(XG_COV(code_coverage_info), (void *) return_value, add_file);
930 }
931 
932 PHP_FUNCTION(xdebug_get_function_count)
933 {
934 	RETURN_LONG(XG_BASE(function_count));
935 }
936 
937 PHP_FUNCTION(xdebug_code_coverage_started)
938 {
939 	RETURN_BOOL(XG_COV(code_coverage_active));
940 }
941 
942 void xdebug_init_coverage_globals(xdebug_coverage_globals_t *xg)
943 {
944 	xg->previous_filename    = NULL;
945 	xg->previous_file        = NULL;
946 	xg->previous_mark_filename = NULL;
947 	xg->previous_mark_file     = NULL;
948 	xg->paths_stack = NULL;
949 	xg->branches.size        = 0;
950 	xg->branches.last_branch_nr = NULL;
951 	xg->code_coverage_active = 0;
952 
953 	/* Get reserved offset */
954 	xg->dead_code_analysis_tracker_offset = zend_xdebug_cc_run_offset;
955 	xg->dead_code_last_start_id = 1;
956 	xg->code_coverage_filter_offset = zend_xdebug_filter_offset;
957 }
958 
959 void xdebug_coverage_count_line_if_active(zend_op_array *op_array, zend_string *file, int lineno)
960 {
961 	if (XG_COV(code_coverage_active) && !op_array->reserved[XG_COV(code_coverage_filter_offset)]) {
962 		xdebug_count_line(file, lineno, 0, 0);
963 	}
964 }
965 
966 void xdebug_coverage_count_line_if_branch_check_active(zend_op_array *op_array, zend_string *file, int lineno)
967 {
968 	if (XG_COV(code_coverage_active) && XG_COV(code_coverage_branch_check)) {
969 		xdebug_coverage_count_line_if_active(op_array, file, lineno);
970 	}
971 }
972 
973 void xdebug_coverage_record_if_active(zend_execute_data *execute_data, zend_op_array *op_array)
974 {
975 	if (!op_array->reserved[XG_COV(code_coverage_filter_offset)] && XG_COV(code_coverage_active)) {
976 		xdebug_print_opcode_info(execute_data, execute_data->opline);
977 	}
978 }
979 
980 void xdebug_coverage_compile_file(zend_op_array *op_array)
981 {
982 	if (XG_COV(code_coverage_active) && XG_COV(code_coverage_unused) && (op_array->fn_flags & ZEND_ACC_DONE_PASS_TWO)) {
983 		xdebug_prefill_code_coverage(op_array);
984 	}
985 }
986 
987 int xdebug_coverage_execute_ex(function_stack_entry *fse, zend_op_array *op_array, zend_string **tmp_filename, char **tmp_function_name)
988 {
989 	xdebug_func func_info;
990 
991 	if (!fse->filtered_code_coverage && XG_COV(code_coverage_active) && XG_COV(code_coverage_unused)) {
992 		char buffer[1024];
993 
994 		*tmp_filename = zend_string_copy(op_array->filename);
995 		xdebug_build_fname_from_oparray(&func_info, op_array);
996 		xdebug_func_format(buffer, sizeof(buffer), &func_info);
997 		*tmp_function_name = xdstrdup(buffer);
998 		xdebug_code_coverage_start_of_function(op_array, *tmp_function_name);
999 
1000 		if (func_info.object_class) {
1001 			zend_string_release(func_info.object_class);
1002 		}
1003 		if (func_info.scope_class) {
1004 			zend_string_release(func_info.scope_class);
1005 		}
1006 		if (func_info.function) {
1007 			xdfree(func_info.function);
1008 		}
1009 		return 1;
1010 	}
1011 
1012 	return 0;
1013 }
1014 
1015 void xdebug_coverage_execute_ex_end(function_stack_entry *fse, zend_op_array *op_array, zend_string *tmp_filename, char *tmp_function_name)
1016 {
1017 	/* Check which path has been used */
1018 	if (!fse->filtered_code_coverage && XG_COV(code_coverage_active) && XG_COV(code_coverage_unused)) {
1019 		xdebug_code_coverage_end_of_function(op_array, tmp_filename, tmp_function_name);
1020 	}
1021 	xdfree(tmp_function_name);
1022 	zend_string_release(tmp_filename);
1023 }
1024 
1025 void xdebug_coverage_init_oparray(zend_op_array *op_array)
1026 {
1027 	function_stack_entry tmp_fse;
1028 
1029 	if (XG_BASE(filter_type_code_coverage) == XDEBUG_FILTER_NONE) {
1030 		op_array->reserved[XG_COV(dead_code_analysis_tracker_offset)] = 0;
1031 		return;
1032 	}
1033 
1034 	tmp_fse.filename = op_array->filename;
1035 	xdebug_build_fname_from_oparray(&tmp_fse.function, op_array);
1036 	xdebug_filter_run_internal(&tmp_fse, XDEBUG_FILTER_CODE_COVERAGE, &tmp_fse.filtered_code_coverage, XG_BASE(filter_type_code_coverage), XG_BASE(filters_code_coverage));
1037 	xdebug_func_dtor_by_ref(&tmp_fse.function);
1038 
1039 	op_array->reserved[XG_COV(code_coverage_filter_offset)] = (void*) (size_t) tmp_fse.filtered_code_coverage;
1040 }
1041 
1042 static int xdebug_switch_handler(XDEBUG_OPCODE_HANDLER_ARGS)
1043 {
1044 	const zend_op *cur_opcode = execute_data->opline;
1045 
1046 	if (!XG_COV(code_coverage_active)) {
1047 		return xdebug_call_original_opcode_handler_if_set(cur_opcode->opcode, XDEBUG_OPCODE_HANDLER_ARGS_PASSTHRU);
1048 	}
1049 
1050 	execute_data->opline++;
1051 	return ZEND_USER_OPCODE_CONTINUE;
1052 }
1053 
1054 
1055 void xdebug_coverage_minit(INIT_FUNC_ARGS)
1056 {
1057 	int i;
1058 
1059 	/* Get reserved offsets */
1060 #if PHP_VERSION_ID >= 80000
1061 	zend_xdebug_cc_run_offset = zend_get_resource_handle(XDEBUG_NAME);
1062 	zend_xdebug_filter_offset = zend_get_resource_handle(XDEBUG_NAME);
1063 #else
1064 	zend_extension dummy_ext;
1065 	zend_xdebug_cc_run_offset = zend_get_resource_handle(&dummy_ext);
1066 	zend_xdebug_filter_offset = zend_get_resource_handle(&dummy_ext);
1067 #endif
1068 
1069 	xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN, xdebug_common_override_handler);
1070 	xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN_DIM, xdebug_common_override_handler);
1071 	xdebug_register_with_opcode_multi_handler(ZEND_ASSIGN_OBJ, xdebug_common_override_handler);
1072 	xdebug_register_with_opcode_multi_handler(ZEND_QM_ASSIGN, xdebug_common_override_handler);
1073 	xdebug_register_with_opcode_multi_handler(ZEND_INCLUDE_OR_EVAL, xdebug_coverage_include_or_eval_handler);
1074 
1075 	/* Overload opcodes for code coverage */
1076 	xdebug_set_opcode_handler(ZEND_JMP, xdebug_common_override_handler);
1077 	xdebug_set_opcode_handler(ZEND_JMPZ, xdebug_common_override_handler);
1078 	xdebug_set_opcode_handler(ZEND_JMPZ_EX, xdebug_common_override_handler);
1079 	xdebug_set_opcode_handler(ZEND_JMPNZ, xdebug_common_override_handler);
1080 	xdebug_set_opcode_handler(ZEND_IS_IDENTICAL, xdebug_common_override_handler);
1081 	xdebug_set_opcode_handler(ZEND_IS_NOT_IDENTICAL, xdebug_common_override_handler);
1082 	xdebug_set_opcode_handler(ZEND_IS_EQUAL, xdebug_common_override_handler);
1083 	xdebug_set_opcode_handler(ZEND_IS_NOT_EQUAL, xdebug_common_override_handler);
1084 	xdebug_set_opcode_handler(ZEND_IS_SMALLER, xdebug_common_override_handler);
1085 	xdebug_set_opcode_handler(ZEND_IS_SMALLER_OR_EQUAL, xdebug_common_override_handler);
1086 	xdebug_set_opcode_handler(ZEND_BOOL_NOT, xdebug_common_override_handler);
1087 
1088 	xdebug_set_opcode_handler(ZEND_ADD, xdebug_common_override_handler);
1089 	xdebug_set_opcode_handler(ZEND_SUB, xdebug_common_override_handler);
1090 	xdebug_set_opcode_handler(ZEND_MUL, xdebug_common_override_handler);
1091 	xdebug_set_opcode_handler(ZEND_DIV, xdebug_common_override_handler);
1092 
1093 	xdebug_set_opcode_handler(ZEND_ADD_ARRAY_ELEMENT, xdebug_common_override_handler);
1094 	xdebug_set_opcode_handler(ZEND_RETURN, xdebug_common_override_handler);
1095 	xdebug_set_opcode_handler(ZEND_RETURN_BY_REF, xdebug_common_override_handler);
1096 	xdebug_set_opcode_handler(ZEND_EXT_STMT, xdebug_common_override_handler);
1097 	xdebug_set_opcode_handler(ZEND_SEND_VAR, xdebug_common_override_handler);
1098 	xdebug_set_opcode_handler(ZEND_SEND_VAR_NO_REF, xdebug_common_override_handler);
1099 	xdebug_set_opcode_handler(ZEND_SEND_VAR_NO_REF_EX, xdebug_common_override_handler);
1100 	xdebug_set_opcode_handler(ZEND_SEND_REF, xdebug_common_override_handler);
1101 	xdebug_set_opcode_handler(ZEND_SEND_VAL, xdebug_common_override_handler);
1102 	xdebug_set_opcode_handler(ZEND_SEND_VAL_EX, xdebug_common_override_handler);
1103 	xdebug_set_opcode_handler(ZEND_SEND_VAR_EX, xdebug_common_override_handler);
1104 	xdebug_set_opcode_handler(ZEND_NEW, xdebug_common_override_handler);
1105 	xdebug_set_opcode_handler(ZEND_EXT_FCALL_BEGIN, xdebug_common_override_handler);
1106 	xdebug_set_opcode_handler(ZEND_INIT_METHOD_CALL, xdebug_common_override_handler);
1107 	xdebug_set_opcode_handler(ZEND_INIT_STATIC_METHOD_CALL, xdebug_common_override_handler);
1108 	xdebug_set_opcode_handler(ZEND_INIT_FCALL, xdebug_common_override_handler);
1109 	xdebug_set_opcode_handler(ZEND_INIT_NS_FCALL_BY_NAME, xdebug_common_override_handler);
1110 	xdebug_set_opcode_handler(ZEND_CATCH, xdebug_common_override_handler);
1111 	xdebug_set_opcode_handler(ZEND_BOOL, xdebug_common_override_handler);
1112 	xdebug_set_opcode_handler(ZEND_INIT_ARRAY, xdebug_common_override_handler);
1113 	xdebug_set_opcode_handler(ZEND_FETCH_DIM_R, xdebug_common_override_handler);
1114 	xdebug_set_opcode_handler(ZEND_FETCH_DIM_W, xdebug_common_override_handler);
1115 	xdebug_set_opcode_handler(ZEND_FETCH_OBJ_R, xdebug_common_override_handler);
1116 	xdebug_set_opcode_handler(ZEND_FETCH_OBJ_W, xdebug_common_override_handler);
1117 	xdebug_set_opcode_handler(ZEND_FETCH_OBJ_FUNC_ARG, xdebug_common_override_handler);
1118 	xdebug_set_opcode_handler(ZEND_FETCH_DIM_FUNC_ARG, xdebug_common_override_handler);
1119 	xdebug_set_opcode_handler(ZEND_FETCH_STATIC_PROP_FUNC_ARG, xdebug_common_override_handler);
1120 	xdebug_set_opcode_handler(ZEND_FETCH_DIM_UNSET, xdebug_common_override_handler);
1121 	xdebug_set_opcode_handler(ZEND_FETCH_OBJ_UNSET, xdebug_common_override_handler);
1122 	xdebug_set_opcode_handler(ZEND_FETCH_CLASS, xdebug_common_override_handler);
1123 	xdebug_set_opcode_handler(ZEND_FETCH_CONSTANT, xdebug_common_override_handler);
1124 	xdebug_set_opcode_handler(ZEND_FETCH_CLASS_CONSTANT, xdebug_common_override_handler);
1125 	xdebug_set_opcode_handler(ZEND_CONCAT, xdebug_common_override_handler);
1126 	xdebug_set_opcode_handler(ZEND_FAST_CONCAT, xdebug_common_override_handler);
1127 	xdebug_set_opcode_handler(ZEND_ISSET_ISEMPTY_DIM_OBJ, xdebug_common_override_handler);
1128 	xdebug_set_opcode_handler(ZEND_ISSET_ISEMPTY_PROP_OBJ, xdebug_common_override_handler);
1129 	xdebug_set_opcode_handler(ZEND_CASE, xdebug_common_override_handler);
1130 	xdebug_set_opcode_handler(ZEND_DECLARE_LAMBDA_FUNCTION, xdebug_common_override_handler);
1131 #if PHP_VERSION_ID < 70400
1132 	xdebug_set_opcode_handler(ZEND_ADD_TRAIT, xdebug_common_override_handler);
1133 	xdebug_set_opcode_handler(ZEND_BIND_TRAITS, xdebug_common_override_handler);
1134 #endif
1135 	xdebug_set_opcode_handler(ZEND_INSTANCEOF, xdebug_common_override_handler);
1136 	xdebug_set_opcode_handler(ZEND_FAST_RET, xdebug_common_override_handler);
1137 	xdebug_set_opcode_handler(ZEND_ROPE_ADD, xdebug_common_override_handler);
1138 	xdebug_set_opcode_handler(ZEND_ROPE_END, xdebug_common_override_handler);
1139 	xdebug_set_opcode_handler(ZEND_COALESCE, xdebug_common_override_handler);
1140 	xdebug_set_opcode_handler(ZEND_TYPE_CHECK, xdebug_common_override_handler);
1141 	xdebug_set_opcode_handler(ZEND_GENERATOR_CREATE, xdebug_common_override_handler);
1142 	xdebug_set_opcode_handler(ZEND_BIND_STATIC, xdebug_common_override_handler);
1143 	xdebug_set_opcode_handler(ZEND_BIND_LEXICAL, xdebug_common_override_handler);
1144 #if PHP_VERSION_ID >= 70400
1145 	xdebug_set_opcode_handler(ZEND_DECLARE_CLASS, xdebug_common_override_handler);
1146 	xdebug_set_opcode_handler(ZEND_DECLARE_CLASS_DELAYED, xdebug_common_override_handler);
1147 #endif
1148 	xdebug_set_opcode_handler(ZEND_SWITCH_STRING, xdebug_switch_handler);
1149 	xdebug_set_opcode_handler(ZEND_SWITCH_LONG, xdebug_switch_handler);
1150 
1151 	/* Override all the other opcodes so that we can mark when we hit a branch
1152 	 * start one */
1153 	for (i = 0; i < 256; i++) {
1154 		if (i == ZEND_HANDLE_EXCEPTION) {
1155 			continue;
1156 		}
1157 		if (!xdebug_isset_opcode_handler(i)) {
1158 			xdebug_set_opcode_handler(i, xdebug_check_branch_entry_handler);
1159 		}
1160 	}
1161 }
1162 
1163 void xdebug_coverage_register_constants(INIT_FUNC_ARGS)
1164 {
1165 	REGISTER_LONG_CONSTANT("XDEBUG_CC_UNUSED", XDEBUG_CC_OPTION_UNUSED, CONST_CS | CONST_PERSISTENT);
1166 	REGISTER_LONG_CONSTANT("XDEBUG_CC_DEAD_CODE", XDEBUG_CC_OPTION_DEAD_CODE, CONST_CS | CONST_PERSISTENT);
1167 	REGISTER_LONG_CONSTANT("XDEBUG_CC_BRANCH_CHECK", XDEBUG_CC_OPTION_BRANCH_CHECK, CONST_CS | CONST_PERSISTENT);
1168 }
1169 
1170 void xdebug_coverage_rinit(void)
1171 {
1172 	XG_COV(code_coverage_active) = 0;
1173 	XG_COV(code_coverage_info) = xdebug_hash_alloc(32, xdebug_coverage_file_dtor);
1174 	XG_COV(dead_code_analysis_tracker_offset) = zend_xdebug_cc_run_offset;
1175 	XG_COV(dead_code_last_start_id) = 1;
1176 	XG_COV(code_coverage_filter_offset) = zend_xdebug_filter_offset;
1177 	XG_COV(previous_filename) = NULL;
1178 	XG_COV(previous_file) = NULL;
1179 	XG_COV(prefill_function_count) = 0;
1180 	XG_COV(prefill_class_count) = 0;
1181 
1182 	/* Initialize visited classes and branches hash */
1183 	XG_COV(visited_branches) = xdebug_hash_alloc(2048, NULL);
1184 
1185 	XG_COV(paths_stack) = xdebug_path_info_ctor();
1186 	XG_COV(branches).size = 0;
1187 	XG_COV(branches).last_branch_nr = NULL;
1188 }
1189 
1190 void xdebug_coverage_post_deactivate(void)
1191 {
1192 	XG_COV(code_coverage_active) = 0;
1193 
1194 	xdebug_hash_destroy(XG_COV(code_coverage_info));
1195 	XG_COV(code_coverage_info) = NULL;
1196 
1197 	xdebug_hash_destroy(XG_COV(visited_branches));
1198 	XG_COV(visited_branches) = NULL;
1199 
1200 	/* Clean up path coverage array */
1201 	if (XG_COV(paths_stack)) {
1202 		xdebug_path_info_dtor(XG_COV(paths_stack));
1203 		XG_COV(paths_stack) = NULL;
1204 	}
1205 	if (XG_COV(branches).last_branch_nr) {
1206 		free(XG_COV(branches).last_branch_nr);
1207 		XG_COV(branches).last_branch_nr = NULL;
1208 		XG_COV(branches).size = 0;
1209 	}
1210 
1211 	if (XG_COV(previous_filename)) {
1212 		zend_string_release(XG_COV(previous_filename));
1213 		XG_COV(previous_filename) = NULL;
1214 	}
1215 	if (XG_COV(previous_mark_filename)) {
1216 		zend_string_release(XG_COV(previous_mark_filename));
1217 		XG_COV(previous_mark_filename) = NULL;
1218 	}
1219 }
1220