[xdebug-dev] xdebug xdebug/xdebug_set.c xdebug/xdebug_set.h xdebug/config.m4 xdebug/php_xdebug.h xdebug/xdebug.c xdebug/xdebug_code_coverage.c xdebug/xdebug_code_coverage.h xdebug/xdebug_private.h - Implemented dead code analysis that should give much better code coverage

From: Derick Rethans <derick[@]derickrethans.nl>
Date: Mon, 25 Sep 2006 22:20:01 +0200

Date: Mon Sep 25 22:20:01 CEST 2006
User: Derick Rethans
Directory: xdebug

Log Message:
[12.00]
- Implemented dead code analysis that should give much better code coverage
  results.

Modified files:
           xdebug/config.m4 (version: 1.23)
           xdebug/php_xdebug.h (version: 1.114)
           xdebug/xdebug.c (version: 1.343)
           xdebug/xdebug_code_coverage.c (version: 1.21)
           xdebug/xdebug_code_coverage.h (version: 1.9)
           xdebug/xdebug_private.h (version: 1.20)
Added files:
           xdebug/xdebug_set.c (new version: 1.1)
           xdebug/xdebug_set.h (new version: 1.1)

[FILE: /xdebug/xdebug_set.c]

/*
   +----------------------------------------------------------------------+
   | Xdebug |
   +----------------------------------------------------------------------+
   | Copyright (c) 2002, 2003, 2004, 2005, 2006 Derick Rethans |
   +----------------------------------------------------------------------+
   | This source file is subject to version 1.0 of the Xdebug license, |
   | that is bundled with this package in the file LICENSE, and is |
   | available at through the world-wide-web at |
   | http://xdebug.derickrethans.nl/license.php |
   | If you did not receive a copy of the Xdebug license and are unable |
   | to obtain it through the world-wide-web, please send a note to |
   | xdebug[@]derickrethans.nl so we can mail you a copy immediately. |
   +----------------------------------------------------------------------+
   | Authors: Derick Rethans <derick[@]xdebug.org> |
   +----------------------------------------------------------------------+
 */
/* $Id: cvstemp,v 1.1 2006/09/25 20:20:01 derick Exp $ */

#include <stdlib.h>
#include <math.h>
#include "xdebug_set.h"

xdebug_set *xdebug_set_create(unsigned int size)
{
        xdebug_set *tmp;

        tmp = calloc(1, sizeof(xdebug_set));
        tmp->size = size;
        size = ceil((size + 7) / 8);
        tmp->setinfo = calloc(1, size);

        return tmp;
}

void xdebug_set_free(xdebug_set *set)
{
        free(set->setinfo);
        free(set);
}

void xdebug_set_add(xdebug_set *set, unsigned int position)
{
        char *byte;
        unsigned int bit;

        byte = &(set->setinfo[position / 8]);
        bit = position % 8;

        *byte = *byte | 1 << bit;
}

void xdebug_set_remove(xdebug_set *set, unsigned int position)
{
        char *byte;
        unsigned int bit;

        byte = &(set->setinfo[position / 8]);
        bit = position % 8;

        *byte = *byte & ~(1 << bit);
}

int xdebug_set_in_ex(xdebug_set *set, unsigned int position, int noisy)
{
        char *byte;
        unsigned int bit;

        byte = &(set->setinfo[position / 8]);
        bit = position % 8;

        return (*byte & (1 << bit));
}

[FILE: /xdebug/xdebug_set.h]

/*
   +----------------------------------------------------------------------+
   | Xdebug |
   +----------------------------------------------------------------------+
   | Copyright (c) 2002, 2003, 2004, 2005, 2006 Derick Rethans |
   +----------------------------------------------------------------------+
   | This source file is subject to version 1.0 of the Xdebug license, |
   | that is bundled with this package in the file LICENSE, and is |
   | available at through the world-wide-web at |
   | http://xdebug.derickrethans.nl/license.php |
   | If you did not receive a copy of the Xdebug license and are unable |
   | to obtain it through the world-wide-web, please send a note to |
   | xdebug[@]derickrethans.nl so we can mail you a copy immediately. |
   +----------------------------------------------------------------------+
   | Authors: Derick Rethans <derick[@]xdebug.org> |
   +----------------------------------------------------------------------+
 */
/* $Id: cvstemp,v 1.1 2006/09/25 20:20:01 derick Exp $ */

#ifndef __XDEBUG_SET_H__
#define __XDEBUG_SET_H__

typedef struct _xdebug_set {
        unsigned int size;
        unsigned char *setinfo;
} xdebug_set;

xdebug_set *xdebug_set_create(unsigned int size);
void xdebug_set_add(xdebug_set *set, unsigned int position);
void xdebug_set_remove(xdebug_set *set, unsigned int position);
#define xdebug_set_in(x,y) xdebug_set_in_ex(x,y,1)
int xdebug_set_in_ex(xdebug_set *set, unsigned int position, int noisy);
void xdebug_set_dump(xdebug_set *set);

#endif

[FILE: /xdebug/config.m4]

===================================================================
RCS file: cvstemp,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- xdebug/config.m4:1.22 Sat Dec 31 09:50:10 2005 GMT
+++ xdebug/config.m4 Mon Sep 25 18:20:01 2006 GMT
@@ -1,4 +1,4 @@
-dnl $Id: cvstemp,v 1.22 2005/12/31 10:50:10 derick Exp $
+dnl $Id: cvstemp,v 1.23 2006/09/25 20:20:01 derick Exp $
 dnl config.m4 for extension xdebug
 
 PHP_ARG_ENABLE(xdebug, whether to enable eXtended debugging support,
@@ -9,7 +9,7 @@
 dnl much out.
   CFLAGS=`echo $CFLAGS | sed 's/O2/O0/'`
   
- PHP_NEW_EXTENSION(xdebug, xdebug.c xdebug_code_coverage.c xdebug_com.c xdebug_compat.c xdebug_handler_dbgp.c xdebug_handler_gdb.c xdebug_handler_php3.c xdebug_handlers.c xdebug_llist.c xdebug_hash.c xdebug_private.c xdebug_profiler.c xdebug_str.c xdebug_superglobals.c xdebug_var.c xdebug_xml.c usefulstuff.c, $ext_shared)
+ PHP_NEW_EXTENSION(xdebug, xdebug.c xdebug_code_coverage.c xdebug_com.c xdebug_compat.c xdebug_handler_dbgp.c xdebug_handler_gdb.c xdebug_handler_php3.c xdebug_handlers.c xdebug_llist.c xdebug_hash.c xdebug_private.c xdebug_profiler.c xdebug_set.c xdebug_str.c xdebug_superglobals.c xdebug_var.c xdebug_xml.c usefulstuff.c, $ext_shared)
   AC_DEFINE(HAVE_XDEBUG,1,[ ])
 
 dnl Check for new current_execute_data field in zend_executor_globals

[FILE: /xdebug/php_xdebug.h]

===================================================================
RCS file: cvstemp,v
retrieving revision 1.113
retrieving revision 1.114
diff -u -r1.113 -r1.114
--- xdebug/php_xdebug.h:1.113 Tue Sep 05 12:46:20 2006 GMT
+++ xdebug/php_xdebug.h Mon Sep 25 18:20:01 2006 GMT
@@ -150,6 +150,7 @@
         zend_bool do_code_coverage;
         xdebug_hash *code_coverage;
         zend_bool code_coverage_unused;
+ zend_bool code_coverage_dead_code_analysis;
         xdebug_hash *code_coverage_op_array_cache;
         unsigned int function_count;
 

[FILE: /xdebug/xdebug.c]

===================================================================
RCS file: cvstemp,v
retrieving revision 1.342
retrieving revision 1.343
diff -u -r1.342 -r1.343
--- xdebug/xdebug.c:1.342 Thu Sep 21 07:36:50 2006 GMT
+++ xdebug/xdebug.c Mon Sep 25 18:20:01 2006 GMT
@@ -462,7 +462,7 @@
                 file = op_array->filename; \
                 file_len = strlen(file); \
 \
- xdebug_count_line(file, lineno, 0 TSRMLS_CC); \
+ xdebug_count_line(file, lineno, 0, 0 TSRMLS_CC); \
         } \
         return ZEND_USER_OPCODE_DISPATCH; \
 }
@@ -487,7 +487,7 @@
                 file = op_array->filename; \
                 file_len = strlen(file); \
 \
- xdebug_count_line(file, lineno, 0 TSRMLS_CC); \
+ xdebug_count_line(file, lineno, 0, 0 TSRMLS_CC); \
         } \
         return old_##f##_handler(ZEND_OPCODE_HANDLER_ARGS_PASSTHRU); \
 }
@@ -576,6 +576,7 @@
         REGISTER_LONG_CONSTANT("XDEBUG_TRACE_HTML", XDEBUG_TRACE_OPTION_HTML, CONST_CS | CONST_PERSISTENT);
 
         REGISTER_LONG_CONSTANT("XDEBUG_CC_UNUSED", XDEBUG_CC_OPTION_UNUSED, CONST_CS | CONST_PERSISTENT);
+ REGISTER_LONG_CONSTANT("XDEBUG_CC_DEAD_CODE", XDEBUG_CC_OPTION_DEAD_CODE, CONST_CS | CONST_PERSISTENT);
 
         XG(breakpoint_count) = 0;
         return SUCCESS;
@@ -1026,7 +1027,7 @@
         }
 
         if (XG(do_code_coverage)) {
- xdebug_count_line(tmp->filename, tmp->lineno, 0 TSRMLS_CC);
+ xdebug_count_line(tmp->filename, tmp->lineno, 0, 0 TSRMLS_CC);
         }
 
         if (XG(profiler_aggregate)) {
@@ -2693,7 +2694,7 @@
         file_len = strlen(file);
 
         if (XG(do_code_coverage)) {
- xdebug_count_line(file, lineno, 0 TSRMLS_CC);
+ xdebug_count_line(file, lineno, 0, 0 TSRMLS_CC);
         }
 
         if (XG(remote_enabled)) {

[FILE: /xdebug/xdebug_code_coverage.c]

===================================================================
RCS file: cvstemp,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -r1.20 -r1.21
--- xdebug/xdebug_code_coverage.c:1.20 Sun Sep 10 16:43:41 2006 GMT
+++ xdebug/xdebug_code_coverage.c Mon Sep 25 18:20:01 2006 GMT
@@ -18,6 +18,7 @@
 
 #include "php_xdebug.h"
 #include "xdebug_private.h"
+#include "xdebug_set.h"
 #include "xdebug_var.h"
 #include "xdebug_code_coverage.h"
 
@@ -39,7 +40,7 @@
         xdfree(file);
 }
 
-void xdebug_count_line(char *filename, int lineno, int executable TSRMLS_DC)
+void xdebug_count_line(char *filename, int lineno, int executable, int deadcode TSRMLS_DC)
 {
         xdebug_coverage_file *file;
         xdebug_coverage_line *line;
@@ -69,7 +70,11 @@
         }
 
         if (executable) {
- line->executable = 1;
+ if (line->executable != 1 && deadcode) {
+ line->executable = 2;
+ } else {
+ line->executable = 1;
+ }
         } else {
                 line->count++;
         }
@@ -77,7 +82,7 @@
         xdfree(sline);
 }
 
-static void prefil_from_opcode(function_stack_entry *fse, char *fn, zend_op opcode TSRMLS_DC)
+static void prefil_from_opcode(function_stack_entry *fse, char *fn, zend_op opcode, int deadcode TSRMLS_DC)
 {
         if (
                 opcode.opcode != ZEND_NOP &&
@@ -88,14 +93,113 @@
                 && opcode.opcode != ZEND_VERIFY_ABSTRACT_CLASS
 #endif
         ) {
- xdebug_count_line(fn, opcode.lineno, 1 TSRMLS_CC);
+ xdebug_count_line(fn, opcode.lineno, 1, deadcode TSRMLS_CC);
         }
 }
 
+static zend_brk_cont_element* xdebug_find_brk_cont(zval *nest_levels_zval, int array_offset, zend_op_array *op_array)
+{
+ int nest_levels;
+ zend_brk_cont_element *jmp_to;
+
+ nest_levels = nest_levels_zval->value.lval;
+
+ do {
+ jmp_to = &op_array->brk_cont_array[array_offset];
+ array_offset = jmp_to->parent;
+ } while (--nest_levels > 0);
+ return jmp_to;
+}
+
+static int xdebug_find_jump(zend_op_array *opa, unsigned int position, unsigned int *jmp1, unsigned int *jmp2)
+{
+ zend_uint base_address = (zend_uint) &(opa->opcodes[0]);
+
+ zend_op opcode = opa->opcodes[position];
+ if (opcode.opcode == ZEND_JMP) {
+ *jmp1 = (opcode.op1.u.opline_num - base_address) / sizeof(zend_op);
+ return 1;
+ } else if (
+ opcode.opcode == ZEND_JMPZ ||
+ opcode.opcode == ZEND_JMPNZ ||
+ opcode.opcode == ZEND_JMPZ_EX ||
+ opcode.opcode == ZEND_JMPNZ_EX
+ ) {
+ *jmp1 = position + 1;
+ *jmp2 = (opcode.op2.u.opline_num - base_address) / sizeof(zend_op);
+ return 1;
+ } else if (opcode.opcode == ZEND_JMPZNZ) {
+ *jmp1 = opcode.op2.u.opline_num;
+ *jmp2 = opcode.extended_value;
+ return 1;
+ } else if (opcode.opcode == ZEND_BRK || opcode.opcode == ZEND_CONT) {
+ zend_brk_cont_element *el;
+
+ if (opcode.op2.op_type == IS_CONST) {
+ el = xdebug_find_brk_cont(&opcode.op2.u.constant, opcode.op1.u.opline_num, opa);
+ *jmp1 = opcode.opcode == ZEND_BRK ? el->brk : el->cont;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void xdebug_analyse_branch(zend_op_array *opa, unsigned int position, xdebug_set *set)
+{
+ unsigned int jump_found = 0;
+ int jump_pos1 = -1;
+ int jump_pos2 = -1;
+
+ /*(fprintf(stderr, "Branch analysis from position: %d\n", position);)*/
+ /* First we see if the branch has been visited, if so we bail out. */
+ if (xdebug_set_in(set, position)) {
+ return;
+ }
+ /* Loop over the opcodes until the end of the array, or until a jump point has been found */
+ xdebug_set_add(set, position);
+ /*(fprintf(stderr, "XDEBUG Adding %d\n", position);)*/
+ while (position < opa->size) {
+
+ /* See if we have a jump instruction */
+ if (xdebug_find_jump(opa, position, &jump_pos1, &jump_pos2)) {
+ /*(fprintf(stderr, "XDEBUG Jump found. Position 1 = %d", jump_pos1);)*/
+ if (jump_pos2 != -1) {
+ /*(fprintf(stderr, ", Position 2 = %d\n", jump_pos2);)*/
+ } else {
+ /*(fprintf(stderr, "\n");)*/
+ }
+ xdebug_analyse_branch(opa, jump_pos1, set);
+ if (jump_pos2 != -1) {
+ xdebug_analyse_branch(opa, jump_pos2, set);
+ }
+ break;
+ }
+ /* See if we have an exit instruction */
+ if (opa->opcodes[position].opcode == ZEND_EXIT) {
+ /* fprintf(stderr, "X* Return found\n"); */
+ break;
+ }
+ /* See if we have a return instruction */
+ if (opa->opcodes[position].opcode == ZEND_RETURN) {
+ /*(fprintf(stderr, "XDEBUG Return found\n");)*/
+ break;
+ }
+ /* See if we have a throw instruction */
+ if (opa->opcodes[position].opcode == ZEND_THROW) {
+ /* fprintf(stderr, "X/* Throw found\n"); */
+ break;
+ }
+
+ position++;
+ /*(fprintf(stderr, "XDEBUG Adding %d\n", position);)*/
+ xdebug_set_add(set, position);
+ }
+}
 static void prefil_from_oparray(function_stack_entry *fse, char *fn, zend_op_array *opa TSRMLS_DC)
 {
- unsigned int i, check_jumps = 0, jmp, marker, end, jump_over = 0;
+ unsigned int i;
         zend_uint base_address = (zend_uint) &(opa->opcodes[0]);
+ xdebug_set *set = NULL;
 
 #ifdef ZEND_ENGINE_2
         /* Check for abstract methods and simply return from this function in those
@@ -106,52 +210,20 @@
         }
 #endif
 
- /* We need to figure out the last jump point to see if we can fix the
- * return at the end of the function. We only have to do that if the
- * last 5 opcodes are EXT_STMT, RETURN, EXT_STMT, RETURN and
- * ZEND_HANDLE_EXCEPTION though (for PHP 5). */
- if (opa->size >= 4) {
- if (
- opa->opcodes[opa->size - 4].opcode == ZEND_RETURN &&
- opa->opcodes[opa->size - 3].opcode == ZEND_EXT_STMT &&
- opa->opcodes[opa->size - 2].opcode == ZEND_RETURN &&
- opa->opcodes[opa->size - 1].opcode == ZEND_HANDLE_EXCEPTION
- ) {
- marker = opa->size - 5;
- check_jumps = 1;
- }
+ /* Run dead code analysis if requested */
+ if (XG(code_coverage_dead_code_analysis)) {
+ set = xdebug_set_create(opa->size);
+ xdebug_analyse_branch(opa, 0, set);
         }
+
+ /* The normal loop then finally */
         for (i = 0; i < opa->size; i++) {
                 zend_op opcode = opa->opcodes[i];
- if (check_jumps) {
- jmp = 0;
- if (opcode.opcode == ZEND_JMP) {
- jmp = (opcode.op1.u.opline_num - base_address) / sizeof(zend_op);
- } else if (
- opcode.opcode == ZEND_JMPZ ||
- opcode.opcode == ZEND_JMPNZ ||
- opcode.opcode == ZEND_JMPZ_EX ||
- opcode.opcode == ZEND_JMPNZ_EX
- ) {
- jmp = (opcode.op2.u.opline_num - base_address) / sizeof(zend_op);
- } else if (opcode.opcode == ZEND_JMPZNZ) {
- jmp = opcode.op2.u.opline_num;
- }
- if (jmp > marker) {
- jump_over = 1;
- }
- }
- }
- if (jump_over && check_jumps) {
- end = opa->size;
- } else {
- end = opa->size - 3;
+ prefil_from_opcode(NULL, fn, opcode, set ? !xdebug_set_in(set, i) : 0 TSRMLS_CC);
         }
 
- /* The normal loop then finally */
- for (i = 0; i < end; i++) {
- zend_op opcode = opa->opcodes[i];
- prefil_from_opcode(NULL, fn, opcode TSRMLS_CC);
+ if (set) {
+ xdebug_set_free(set);
         }
 }
 
@@ -220,7 +292,8 @@
         if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &options) == FAILURE) {
                 return;
         }
- XG(code_coverage_unused) = (options == XDEBUG_CC_OPTION_UNUSED);
+ XG(code_coverage_unused) = (options & XDEBUG_CC_OPTION_UNUSED);
+ XG(code_coverage_dead_code_analysis) = (options & XDEBUG_CC_OPTION_DEAD_CODE);
 
         if (XG(extended_info)) {
                 XG(do_code_coverage) = 1;
@@ -267,7 +340,7 @@
         zval *retval = (zval*) ret;
 
         if (line->executable && (line->count == 0)) {
- add_index_long(retval, line->lineno, -1);
+ add_index_long(retval, line->lineno, -line->executable);
         } else {
                 add_index_long(retval, line->lineno, 1);
         }

[FILE: /xdebug/xdebug_code_coverage.h]

===================================================================
RCS file: cvstemp,v
retrieving revision 1.8
retrieving revision 1.9
diff -u -r1.8 -r1.9
--- xdebug/xdebug_code_coverage.h:1.8 Sun Jan 01 14:27:27 2006 GMT
+++ xdebug/xdebug_code_coverage.h Mon Sep 25 18:20:01 2006 GMT
@@ -37,7 +37,7 @@
 void xdebug_coverage_line_dtor(void *data);
 void xdebug_coverage_file_dtor(void *data);
 
-void xdebug_count_line(char *file, int lineno, int executable TSRMLS_DC);
+void xdebug_count_line(char *file, int lineno, int executable, int deadcode TSRMLS_DC);
 void xdebug_prefil_code_coverage(function_stack_entry *fse, zend_op_array *op_array TSRMLS_DC);
 
 PHP_FUNCTION(xdebug_start_code_coverage);

[FILE: /xdebug/xdebug_private.h]

===================================================================
RCS file: cvstemp,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- xdebug/xdebug_private.h:1.19 Tue Sep 05 11:05:08 2006 GMT
+++ xdebug/xdebug_private.h Mon Sep 25 18:20:01 2006 GMT
@@ -71,6 +71,7 @@
 #define XDEBUG_TRACE_OPTION_HTML 4
 
 #define XDEBUG_CC_OPTION_UNUSED 1
+#define XDEBUG_CC_OPTION_DEAD_CODE 2
 
 #define STATUS_STARTING 0
 #define STATUS_STOPPING 1
Received on Mon Sep 25 2006 - 22:20:07 BST

This archive was generated by hypermail 2.2.0 : Sun Jun 24 2018 - 04:00:03 BST