Skip to content

Commit d05f958

Browse files
committed
Fixed issue #1674: Inconsistent Path & Branch Coverage Reported
1 parent 65e43ec commit d05f958

23 files changed

+961
-345
lines changed

src/coverage/branch_info.c

Lines changed: 66 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
+----------------------------------------------------------------------+
3-
| Copyright (c) 1997-2020 Derick Rethans |
3+
| Copyright (c) 1997-2025 Derick Rethans |
44
+----------------------------------------------------------------------+
55
| This source file is subject to the 2-Clause BSD license which is |
66
| available through the LICENSE file, or online at |
@@ -33,6 +33,7 @@ xdebug_branch_info *xdebug_branch_info_create(unsigned int size)
3333
tmp->path_info.paths_count = 0;
3434
tmp->path_info.paths_size = 0;
3535
tmp->path_info.paths = NULL;
36+
tmp->highest_out = 0;
3637

3738
return tmp;
3839
}
@@ -249,7 +250,7 @@ static void xdebug_branch_find_path(unsigned int nr, xdebug_branch_info *branch_
249250
int found = 0;
250251
size_t i = 0;
251252

252-
if (branch_info->path_info.paths_count > 4095) {
253+
if (branch_info->path_info.paths_count >= XDEBUG_MAX_PATHS) {
253254
return;
254255
}
255256

@@ -335,23 +336,36 @@ void xdebug_branch_find_paths(xdebug_branch_info *branch_info)
335336
}
336337
}
337338

339+
static int fetch_mark_file(zend_string *filename, xdebug_coverage_file **file)
340+
{
341+
if (XG_COV(previous_mark_filename) && zend_string_equals(XG_COV(previous_mark_filename), filename)) {
342+
*file = XG_COV(previous_mark_file);
343+
return 1;
344+
}
345+
346+
if (!xdebug_hash_find(XG_COV(info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) file)) {
347+
return 0;
348+
}
349+
350+
if (XG_COV(previous_mark_filename)) {
351+
zend_string_release(XG_COV(previous_mark_filename));
352+
}
353+
XG_COV(previous_mark_filename) = zend_string_copy((*file)->name);
354+
XG_COV(previous_mark_file) = *file;
355+
356+
return 1;
357+
}
358+
338359
void xdebug_branch_info_mark_reached(zend_string *filename, char *function_name, zend_op_array *op_array, long opcode_nr)
339360
{
340361
xdebug_coverage_file *file;
341-
xdebug_coverage_function *function;
362+
xdebug_coverage_analysis_function *analysis_function = NULL;
363+
xdebug_coverage_runtime_function *runtime_function;
342364
xdebug_branch_info *branch_info;
343365

344-
if (XG_COV(previous_mark_filename) && zend_string_equals(XG_COV(previous_mark_filename), filename)) {
345-
file = XG_COV(previous_mark_file);
346-
} else {
347-
if (!xdebug_hash_find(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &file)) {
348-
return;
349-
}
350-
if (XG_COV(previous_mark_filename)) {
351-
zend_string_release(XG_COV(previous_mark_filename));
352-
}
353-
XG_COV(previous_mark_filename) = zend_string_copy(file->name);
354-
XG_COV(previous_mark_file) = file;
366+
/* Find the information for the given filename */
367+
if (!fetch_mark_file(filename, &file)) {
368+
return;
355369
}
356370

357371
/* If there is no branch info, we don't have to do more */
@@ -360,11 +374,17 @@ void xdebug_branch_info_mark_reached(zend_string *filename, char *function_name,
360374
}
361375

362376
/* Check if the function already exists in the hash */
363-
if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) {
377+
if (!xdebug_hash_find(file->analysis.functions, function_name, strlen(function_name), (void *) &analysis_function)) {
364378
return;
365379
}
366380

367-
branch_info = function->branch_info;
381+
/* Find the runtime function, and if it does not exist, create it */
382+
if (!xdebug_hash_find(file->runtime.functions, function_name, strlen(function_name), (void *) &runtime_function)) {
383+
runtime_function = xdebug_coverage_runtime_function_ctor(function_name, analysis_function);
384+
xdebug_hash_add(file->runtime.functions, function_name, strlen(function_name), runtime_function);
385+
}
386+
387+
branch_info = analysis_function->branch_info;
368388

369389
if (opcode_nr != 0 && xdebug_set_in(branch_info->entry_points, opcode_nr)) {
370390
xdebug_code_coverage_end_of_function(op_array, filename, function_name);
@@ -382,7 +402,8 @@ void xdebug_branch_info_mark_reached(zend_string *filename, char *function_name,
382402

383403
for (i = 0; i < branch_info->branches[XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))]].outs_count; i++) {
384404
if (branch_info->branches[XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))]].outs[i] == opcode_nr) {
385-
branch_info->branches[XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))]].outs_hit[i] = 1;
405+
//printf("\nOUT HIT: %p (%d) (*%ld) %ld\n", runtime_function->hit_branch, XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))], branch_info->highest_out, i + 1);
406+
xdebug_set_add(runtime_function->hit_branch, (XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))] * (1 + branch_info->highest_out)) + i + 1);
386407
}
387408
}
388409
}
@@ -395,78 +416,65 @@ void xdebug_branch_info_mark_reached(zend_string *filename, char *function_name,
395416
}
396417
xdfree(key);
397418

398-
branch_info->branches[opcode_nr].hit = 1;
419+
//printf("\nIN HIT: %p (%ld) (*%ld) %d\n", runtime_function->hit_branch, opcode_nr, branch_info->highest_out, 0);
420+
xdebug_set_add(runtime_function->hit_branch, opcode_nr * (1 + branch_info->highest_out));
399421

400422
XG_COV(branches).last_branch_nr[XDEBUG_VECTOR_COUNT(XG_BASE(stack))] = opcode_nr;
401423
}
402424
}
403425

426+
/* TODO: Store paths hit differently */
404427
void xdebug_branch_info_mark_end_of_function_reached(zend_string *filename, char *function_name, char *key, int key_len)
405428
{
406429
xdebug_coverage_file *file;
407-
xdebug_coverage_function *function;
408-
xdebug_branch_info *branch_info;
430+
xdebug_coverage_analysis_function *analysis_function;
431+
xdebug_coverage_runtime_function *runtime_function;
409432
xdebug_path *path;
410433

411-
if (XG_COV(previous_mark_filename) && zend_string_equals(XG_COV(previous_mark_filename), filename) == 0) {
412-
file = XG_COV(previous_mark_file);
413-
} else {
414-
if (!xdebug_hash_find(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &file)) {
415-
return;
416-
}
417-
zend_string_release(XG_COV(previous_mark_filename));
418-
XG_COV(previous_mark_filename) = zend_string_copy(file->name);
419-
XG_COV(previous_mark_file) = file;
434+
/* Find the information for the given filename */
435+
if (!fetch_mark_file(filename, &file)) {
436+
return;
420437
}
421438

422439
/* If there is no branch info, we don't have to do more */
423440
if (!file->has_branch_info) {
424441
return;
425442
}
426443

427-
/* Check if the function already exists in the hash */
428-
if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) {
444+
/* Check if the function has been analysed, and hence exists in the hash */
445+
if (!xdebug_hash_find(file->analysis.functions, function_name, strlen(function_name), (void *) &analysis_function)) {
429446
return;
430447
}
431448

432-
branch_info = function->branch_info;
433-
434-
if (!xdebug_hash_find(branch_info->path_info.path_hash, key, key_len, (void *) &path)) {
449+
/* Check whether we know something about the current path */
450+
if (!xdebug_hash_find(analysis_function->branch_info->path_info.path_hash, key, key_len, (void *) &path)) {
435451
return;
436452
}
437-
path->hit = 1;
438-
}
439453

440-
void xdebug_branch_info_add_branches_and_paths(zend_string *filename, char *function_name, xdebug_branch_info *branch_info)
441-
{
442-
xdebug_coverage_file *file;
443-
xdebug_coverage_function *function;
454+
/* Find the runtime function, and if it does not exist, create it */
455+
if (!xdebug_hash_find(file->runtime.functions, function_name, strlen(function_name), (void *) &runtime_function)) {
456+
runtime_function = xdebug_coverage_runtime_function_ctor(function_name, analysis_function);
457+
xdebug_hash_add(file->runtime.functions, function_name, strlen(function_name), runtime_function);
458+
}
444459

445-
if (XG_COV(previous_filename) && zend_string_equals(XG_COV(previous_filename), filename) == 0) {
446-
file = XG_COV(previous_file);
447-
} else {
448-
/* Check if the file already exists in the hash */
449-
if (!xdebug_hash_find(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), (void *) &file)) {
450-
/* The file does not exist, so we add it to the hash */
451-
file = xdebug_coverage_file_ctor(filename);
460+
xdebug_hash_add(runtime_function->hit_paths, key, key_len, NULL);
461+
}
452462

453-
xdebug_hash_add(XG_COV(code_coverage_info), ZSTR_VAL(filename), ZSTR_LEN(filename), file);
454-
}
455-
zend_string_release(XG_COV(previous_filename));
456-
XG_COV(previous_filename) = zend_string_copy(file->name);
457-
XG_COV(previous_file) = file;
458-
}
463+
void xdebug_branch_info_add_branches_and_paths(xdebug_coverage_file *file, char *function_name, xdebug_branch_info *branch_info)
464+
{
465+
xdebug_coverage_analysis_function *analysis_function;
459466

467+
/* TODO: Check if creating things here is actually useful */
460468
/* Check if the function already exists in the hash */
461-
if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) {
462-
/* The file does not exist, so we add it to the hash */
463-
function = xdebug_coverage_function_ctor(function_name);
469+
if (!xdebug_hash_find(file->analysis.functions, function_name, strlen(function_name), (void *) &analysis_function)) {
470+
/* The function does not exist, so we add it to the hash */
471+
analysis_function = xdebug_coverage_analysis_function_ctor(function_name);
464472

465-
xdebug_hash_add(file->functions, function_name, strlen(function_name), function);
473+
xdebug_hash_add(file->analysis.functions, function_name, strlen(function_name), analysis_function);
466474
}
467475

468476
if (branch_info) {
469477
file->has_branch_info = 1;
470478
}
471-
function->branch_info = branch_info;
479+
analysis_function->branch_info = branch_info;
472480
}

src/coverage/branch_info.h

Lines changed: 4 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
+----------------------------------------------------------------------+
33
| Xdebug |
44
+----------------------------------------------------------------------+
5-
| Copyright (c) 2002-2020 Derick Rethans |
5+
| Copyright (c) 2002-2025 Derick Rethans |
66
+----------------------------------------------------------------------+
77
| This source file is subject to version 1.01 of the Xdebug license, |
88
| that is bundled with this package in the file LICENSE, and is |
@@ -32,23 +32,21 @@
3232
#define XDEBUG_JMP_NOT_SET (INT_MAX-1)
3333
#define XDEBUG_JMP_EXIT (INT_MAX-2)
3434

35-
#define XDEBUG_BRANCH_MAX_OUTS 64
35+
#define XDEBUG_MAX_PATHS 4096
36+
#define XDEBUG_BRANCH_MAX_OUTS 64
3637

3738
typedef struct _xdebug_branch {
3839
unsigned int start_lineno;
3940
unsigned int end_lineno;
4041
unsigned int end_op;
41-
unsigned char hit;
4242
unsigned int outs_count;
4343
int outs[XDEBUG_BRANCH_MAX_OUTS];
44-
unsigned char outs_hit[XDEBUG_BRANCH_MAX_OUTS];
4544
} xdebug_branch;
4645

4746
typedef struct _xdebug_path {
4847
unsigned int elements_count;
4948
unsigned int elements_size;
5049
unsigned int *elements;
51-
unsigned char hit;
5250
} xdebug_path;
5351

5452
/* Contains information for paths that belong to a set of branches (as stored in xdebug_branch_info) */
@@ -66,31 +64,8 @@ typedef struct _xdebug_branch_info {
6664
xdebug_set *starts; /* A set of opcodes nrs where each branch starts */
6765
xdebug_set *ends; /* A set of opcodes nrs where each ends starts */
6866
xdebug_branch *branches; /* Information about each branch */
67+
size_t highest_out;
6968

7069
xdebug_path_info path_info; /* The paths that can be created out of these branches */
7170
} xdebug_branch_info;
72-
73-
xdebug_branch_info *xdebug_branch_info_create(unsigned int size);
74-
75-
void xdebug_branch_info_update(xdebug_branch_info *branch_info, unsigned int pos, unsigned int lineno, unsigned int outidx, unsigned int jump_pos);
76-
void xdebug_branch_post_process(zend_op_array *opa, xdebug_branch_info *branch_info);
77-
void xdebug_branch_find_paths(xdebug_branch_info *branch_info);
78-
79-
void xdebug_branch_info_dump(zend_op_array *opa, xdebug_branch_info *branch_info);
80-
void xdebug_branch_info_add_branches_and_paths(zend_string *filename, char *function_name, xdebug_branch_info *branch_info);
81-
void xdebug_branch_info_free(xdebug_branch_info *branch_info);
82-
83-
xdebug_path *xdebug_path_new(xdebug_path *old_path);
84-
void xdebug_path_free(xdebug_path *path);
85-
86-
xdebug_path_info *xdebug_path_info_ctor(void);
87-
void xdebug_path_info_dtor(xdebug_path_info *path_info);
88-
89-
void xdebug_path_info_add_path_for_level(xdebug_path_info *path_info, xdebug_path *path, unsigned int level);
90-
xdebug_path *xdebug_path_info_get_path_for_level(xdebug_path_info *path_info, unsigned int level);
91-
92-
void xdebug_create_key_for_path(xdebug_path *path, xdebug_str *str);
93-
94-
void xdebug_branch_info_mark_reached(zend_string *filename, char *function_name, zend_op_array *op_array, long opcode_nr);
95-
void xdebug_branch_info_mark_end_of_function_reached(zend_string *filename, char *function_name, char *key, int key_len);
9671
#endif

0 commit comments

Comments
 (0)