[xdebug-dev] xdebug xdebug/xdebug_code_coverage.c First commit to branch. Added functions and structures to count instructions.

From: Allister Beharry <allister.beharry[@]gmail.com>
Date: Fri, 17 Jul 2009 08:20:21 +0200

Date: Fri Jul 17 08:20:21 CEST 2009
User: Allister Beharry
Directory: xdebug

Log Message:
First commit to branch. Added functions and structures to count instructions.
Modified files:
           xdebug/xdebug_code_coverage.c (version: 1.53.4.1)

[FILE: /xdebug/xdebug_code_coverage.c]

===================================================================
RCS file: cvstemp,v
retrieving revision 1.53
retrieving revision 1.53.4.1
diff -u -r1.53 -r1.53.4.1
--- xdebug/xdebug_code_coverage.c:1.53 Sat Jul 04 12:31:44 2009 GMT
+++ xdebug/xdebug_code_coverage.c Fri Jul 17 04:20:21 2009 GMT
@@ -22,80 +22,295 @@
 #include "xdebug_var.h"
 #include "xdebug_code_coverage.h"
 #include "xdebug_compat.h"
+#include "srm_oparray.h"
 
 extern ZEND_DECLARE_MODULE_GLOBALS(xdebug);
 
+extern const op_usage opcodes[];
+
+void xdebug_coverage_decision_dtor(void *data)
+{
+ xdebug_coverage_decision *decision = (xdebug_coverage_decision *) data;
+ xdfree(decision);
+}
+
+void xdebug_coverage_instruction_dtor(void *data)
+{
+ xdebug_coverage_instruction *instruction = (xdebug_coverage_instruction *) data;
+ xdfree(instruction->opcode_name);
+ /*
+ if (instruction->decision) {
+ xdebug_set_free(instruction->decision_branch1_set);
+ xdebug_set_free(instruction->decision_branch2_set);
+ }
+ */
+ xdfree(instruction);
+}
+
 void xdebug_coverage_line_dtor(void *data)
 {
         xdebug_coverage_line *line = (xdebug_coverage_line *) data;
-
+ xdebug_hash_destroy(line->instructions);
+ /*
+ if (line->decision) {
+ xdebug_set_free(line->decision_branch1_set);
+ xdebug_set_free(line->decision_branch2_set);
+ }
+ */
         xdfree(line);
 }
 
+void xdebug_coverage_function_dtor(void *data)
+{
+ xdebug_coverage_function *function = (xdebug_coverage_function *) data;
+ xdebug_hash_destroy(function->lines);
+ xdebug_hash_destroy(function->decisions);
+ xdebug_set_free(function->total_branches_set);
+ xdebug_set_free(function->branches_counted_set);
+}
+
 void xdebug_coverage_file_dtor(void *data)
 {
         xdebug_coverage_file *file = (xdebug_coverage_file *) data;
-
- xdebug_hash_destroy(file->lines);
+ xdebug_hash_destroy(file->functions);
         xdfree(file->name);
         xdfree(file);
 }
 
-void xdebug_count_line(char *filename, int lineno, int executable, int deadcode TSRMLS_DC)
+void xdebug_count_instruction(zend_op *op, zend_execute_data *execute_data TSRMLS_DC)
 {
- xdebug_coverage_file *file;
+ xdebug_coverage_file *file;
+ xdebug_coverage_function *function;
         xdebug_coverage_line *line;
- char *sline;
-
- sline = xdebug_sprintf("%d", lineno);
-
- /* Check if the file already exists in the hash */
- if (!xdebug_hash_find(XG(code_coverage), filename, strlen(filename), (void *) &file)) {
- /* The file does not exist, so we add it to the hash, and
- * add a line element to the file */
- file = xdmalloc(sizeof(xdebug_coverage_file));
- file->name = xdstrdup(filename);
- file->lines = xdebug_hash_alloc(128, xdebug_coverage_line_dtor);
-
- xdebug_hash_add(XG(code_coverage), filename, strlen(filename), file);
- }
-
- /* Check if the line already exists in the hash */
- if (!xdebug_hash_find(file->lines, sline, strlen(sline), (void *) &line)) {
- line = xdmalloc(sizeof(xdebug_coverage_line));
- line->lineno = lineno;
- line->count = 0;
- line->executable = 0;
-
- xdebug_hash_add(file->lines, sline, strlen(sline), line);
- }
-
- if (executable) {
- if (line->executable != 1 && deadcode) {
- line->executable = 2;
- } else {
- line->executable = 1;
- }
- } else {
- line->count++;
- }
-
+ xdebug_coverage_instruction *instruction;
+ zend_op_array *op_array = execute_data->op_array;
+ zend_op *base_address = &(op_array->opcodes[0]);
+ int position = (((long) op) - (long) base_address) / sizeof(zend_op), position_in_line;
+ int executed;
+ char *sline = xdebug_sprintf("%d", op->lineno);
+ char *sposition = xdebug_sprintf("%d", position), *sposition_in_line;
+ char *filename = op_array->filename;
+ char *function_name;
+ fprintf(stderr, "XDEBUG_CC: xdebug_count_instruction: starting...sline = %s sposition = %s filename = %s \n", sline, sposition, filename);
+ if (op_array->type == ZEND_USER_FUNCTION) {
+ if (op_array->function_name == NULL)
+ function_name = "0" ;
+ else
+ function_name = op_array->function_name ;
+ fprintf(stderr, "XDEBUG_CC: xdebug_count_instruction: setting function name to %s...\n", function_name);
+ }
+ /*fprintf(stderr, "XDEBUG_CC: xdebug_count_instruction: starting...sline = %s sposition = %s filename = %s \n", sline, sposition, filename);*/
+ if (!xdebug_hash_find(XG(code_coverage), filename, strlen(filename), (void *) &file)) {
+ /*bailout: error*/
+ php_error(E_USER_ERROR, "XDEBUG_CC: xdebug_count_instruction: file does not exist in hash.");
+ return;
+ }
+ /* Check if the function exists in the hash */
+ if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) {
+ /*bailout: error*/
+ php_error(E_USER_ERROR, "XDEBUG_CC: xdebug_count_instruction: function does not exist in hash.");
+ return;
+ }
+ /* Check if the line exists in the hash */
+ if (!xdebug_hash_find(function->lines, sline, strlen(sline), (void *) &line)) {
+ /*bailout: error*/
+ php_error(E_USER_ERROR, "XDEBUG_CC: xdebug_count_instruction: line does not exist in hash.");
+ return;
+ }
+ position_in_line = position - line->previous_total_instructions;
+ sposition_in_line = xdebug_sprintf("%d", position_in_line);
+ /* Check if the instruction already exists in the hash */
+ if (!xdebug_hash_find(line->instructions, sposition_in_line, strlen(sposition_in_line), (void *) &instruction)) {
+ /*bailout: error*/
+ php_error(E_USER_ERROR, xdebug_sprintf("XDEBUG_CC: xdebug_count_instruction: instruction at position %s, position_in_line %s does not exist in hash."), position, position_in_line);
+ return;
+ }
+ fprintf(stderr, "XDEBUG_CC: xdebug_count_instruction: counting instruction at position %s, position_in_line %s, in line %s.\n", sposition, sposition_in_line, sline);
+ executed = instruction->executed;
+ instruction->count++;
+ if (instruction->executed == 0) instruction->executed = 1;
+ //instruction->execute_data = execute_data;
+ if (line->executed == 0) line->executed = 1;
+ if (instruction->position == (line->total_instructions - 1)) line->count++; /*only count the line once when the final instruction executes*/
+
+ if (xdebug_set_in(function->total_branches_set, position)) { /*Instruction is in a decision branch*/
+ if (!xdebug_set_in(function->branches_counted_set, position)) /*Only increment branches_counted first time counting instructions*/
+ {
+ function->branches_counted++;
+ file->branches_counted++;
+ xdebug_set_add(function->branches_counted_set, position);
+ }
+ }
         xdfree(sline);
+ xdfree(sposition);
+ xdfree(sposition_in_line);
 }
 
-static void prefill_from_opcode(char *fn, zend_op opcode, int deadcode TSRMLS_DC)
+static void prefill_from_opcode(char *filename, zend_op_array *op_array, zend_op op, unsigned int position, unsigned int *instructions_prefilled, int deadcode TSRMLS_DC)
 {
- if (
- opcode.opcode != ZEND_NOP &&
- opcode.opcode != ZEND_EXT_NOP &&
- opcode.opcode != ZEND_RECV &&
- opcode.opcode != ZEND_RECV_INIT
- && opcode.opcode != ZEND_VERIFY_ABSTRACT_CLASS
- && opcode.opcode != ZEND_OP_DATA
- && opcode.opcode != ZEND_ADD_INTERFACE
- ) {
- xdebug_count_line(fn, opcode.lineno, 1, deadcode TSRMLS_CC);
- }
+ xdebug_coverage_file *file;
+ xdebug_coverage_function *function;
+ xdebug_coverage_line *line;
+ xdebug_coverage_instruction *instruction;
+ xdebug_coverage_decision *decision;
+ zend_op previous_op;
+ int position_in_line;
+ unsigned int jmp_position1 = 0, jmp_position2 = 0;
+ char *function_name;
+ char *sline = xdebug_sprintf("%d", op.lineno);
+ char *sposition = xdebug_sprintf("%d", position), *sposition_in_line;
+ zend_op *base_address = &(op_array->opcodes[0]);
+ int num_known_opcodes = sizeof(op_array) / sizeof(op_array[0]);
+ /*fprintf(stderr, "XDEBUG_CC: prefill_from_opcode: starting...sline = %s sposition = %s filename = %s \n", sline, xdebug_sprintf("%d", position), filename);*/
+
+ if (op_array->type == ZEND_USER_FUNCTION) {
+ if (op_array->function_name == NULL)
+ function_name = "0" ;
+ else
+ function_name = op_array->function_name ;
+ /*fprintf(stderr, "XDEBUG_CC: xdebug_prefill_opcode: setting function name to %s...\n", function_name);*/
+ } else return; //don'tcount classes as yet
+
+ /* Check if the file already exists in the hash */
+ if (!xdebug_hash_find(XG(code_coverage), filename, strlen(filename), (void *) &file)) {
+ /* The file does not exist, so we add it to the hash, and add a functions element to the file */
+ file = xdmalloc(sizeof(xdebug_coverage_file));
+ file->name = xdstrdup(filename);
+ file->functions = xdebug_hash_alloc(128, xdebug_coverage_function_dtor);
+ file->total_branches = 0;
+ file->branches_counted = 0;
+ fprintf(stderr, "XDEBUG_CC: xdebug_prefill_opcode: adding file with filename=%s to hash...\n", filename);
+ xdebug_hash_add(XG(code_coverage), filename, strlen(filename), file);
+ }
+
+ /* Check if the function already exists in the hash */
+ if (!xdebug_hash_find(file->functions, function_name, strlen(function_name), (void *) &function)) {
+ *instructions_prefilled = 0;
+ function = xdmalloc(sizeof(xdebug_coverage_function));
+ function->name = xdstrdup(function_name);
+ function->dead = 0;
+ function->count = 0;
+ function->executed = 0;
+ function->lines = xdebug_hash_alloc(128, xdebug_coverage_line_dtor);
+ function->total_lines = 0;
+ function->lines_counted = 0;
+ function->decisions = xdebug_hash_alloc(128, xdebug_coverage_decision_dtor);
+ function->total_branches_set = xdebug_set_create(op_array->last);
+ function->total_branches = 0;
+ function->branches_counted = 0;
+ function->branches_counted_set = xdebug_set_create(op_array->last);
+ fprintf(stderr, "XDEBUG_CC: xdebug_prefill_opcode: adding function with name=%s to hash...\n", function->name);
+ xdebug_hash_add(file->functions, function_name, strlen(function_name), function);
+ }
+
+ /* Check if the line already exists in the hash */
+ if (!xdebug_hash_find(function->lines, sline, strlen(sline), (void *) &line)) {
+ *instructions_prefilled = position;
+ line = xdmalloc(sizeof(xdebug_coverage_line));
+ line->count = 0;
+ line->executed = 0;
+ line->lineno = 0;
+ line->dead = 0;
+ line->instructions_counted = 0;
+ line->total_instructions = 0;
+ line->previous_total_instructions = *instructions_prefilled;
+ line->instructions = xdebug_hash_alloc(16, xdebug_coverage_instruction_dtor); /* can be less if max instructions per line < 8*/
+ line->decision = 0;
+ line->decision_branch1_lineno = 0;
+ line->decision_branch2_lineno = 0;
+ fprintf(stderr, "XDEBUG_CC: xdebug_prefill_opcode adding line with lineno=%s and instructions_prefilled=%d to hash...\n", sline, *instructions_prefilled);
+ xdebug_hash_add(function->lines, sline, strlen(sline), line);
+ }
+ position_in_line = position - *instructions_prefilled;
+ sposition_in_line = xdebug_sprintf("%d", position_in_line);
+
+ /* Check if the instruction already exists in the hash */
+ if (xdebug_hash_find(line->instructions, sposition_in_line, strlen(sposition_in_line), (void *) &instruction)) {
+ /*bailout: error*/
+ php_error(E_USER_ERROR, "XDEBUG_CC: prefill_from_opcode: instruction already exists in line hash.");
+ return;
+ }
+ /*Add the instruction to the line*/
+ instruction = xdmalloc(sizeof(xdebug_coverage_instruction));
+ if (op.opcode < 150)
+ instruction->opcode_name = xdstrdup(opcodes[op.opcode].name);
+ else instruction->opcode_name = xdstrdup("UNKNOWN");
+ instruction->op = op;
+ instruction->position = position_in_line;
+ instruction->count = 0;
+ instruction->dead = deadcode;
+ instruction->executed = 0;
+ instruction->lineno = op.lineno;
+ instruction->decision = 0;
+ instruction->decision_branch1_position = 0;
+ instruction->decision_branch2_position = 0;
+ fprintf(stderr, "XDEBUG_CC: xdebug_prefill_opcode adding instruction with position=%s dead=%d lineno=%s instructions_prefilled=%d position_in_line=%d to line hash...\n", sposition,instruction->dead, sline, *instructions_prefilled, position_in_line);
+ xdebug_hash_add(line->instructions, sposition_in_line, strlen(sposition_in_line), instruction);
+ if (line->lineno == 0) line->lineno = instruction->lineno;
+ line->total_instructions++;
+ xdfree(sposition_in_line);
+
+ /*check if this is a decision*/
+ if (
+ op.opcode == ZEND_JMPZ ||
+ op.opcode == ZEND_JMPNZ ||
+ op.opcode == ZEND_JMPZ_EX ||
+ op.opcode == ZEND_JMPNZ_EX ||
+ op.opcode == ZEND_JMPZNZ)
+ {
+ previous_op = op_array->opcodes[position - 1];
+ if (previous_op.opcode == ZEND_EXT_STMT) previous_op = op_array->opcodes[position - 2];
+ if (
+ previous_op.opcode == ZEND_IS_SMALLER ||
+ previous_op.opcode == ZEND_IS_IDENTICAL ||
+ previous_op.opcode == ZEND_IS_NOT_IDENTICAL ||
+ previous_op.opcode == ZEND_IS_EQUAL ||
+ previous_op.opcode == ZEND_IS_NOT_EQUAL ||
+ previous_op.opcode == ZEND_IS_SMALLER)
+ {
+ if (op.opcode == ZEND_JMPZ || op.opcode == ZEND_JMPNZ || op.opcode == ZEND_JMPZ_EX || op.opcode == ZEND_JMPNZ_EX)
+ {
+ jmp_position1 = position + 1;
+ #ifdef ZEND_ENGINE_2
+ jmp_position2 = ((long) op.op2.u.jmp_addr - (long) base_address) / sizeof(zend_op);
+ #else
+ jmp_position2 = op.op1.u.opline_num;
+ #endif
+ }
+ else/*ZEND_JMPZNZ*/
+ {
+ jmp_position1 = op.op2.u.opline_num;
+ jmp_position2 = op.extended_value;
+ }
+ }
+ fprintf(stderr, "XDEBUG_CC: xdebug_prefill_opcode: Found decision with position=%s jmp1=%d jmp2=%d.\n", sposition, jmp_position1, jmp_position2);
+ /*Create a decision andd add it to the function*/
+ decision = xdmalloc(sizeof(xdebug_coverage_decision));
+ decision->instruction_position = position;
+ decision->lineno = op.lineno;
+ decision->branch1_position = jmp_position1;
+ decision->branch1_lineno = op_array->opcodes[jmp_position1].lineno;
+ decision->branch1_set = xdebug_set_create(op_array->last);
+ decision->branch2_position = jmp_position2;
+ decision->branch2_lineno = op_array->opcodes[jmp_position2].lineno;
+ decision->branch2_set = xdebug_set_create(op_array->last);
+ fprintf(stderr, "XDEBUG_CC: xdebug_prefill_opcode adding decision with position=%d branch1_position=%d branch2_position=%d to hash...\n", position, jmp_position1, jmp_position2);
+ xdebug_hash_add(function->decisions, sposition, strlen(sposition), decision);
+ xdebug_set_add(function->total_branches_set, jmp_position1);
+ xdebug_set_add(function->total_branches_set, jmp_position2);
+ function->total_branches += 2;
+ file->total_branches += 2;
+ /*Also add the decision to the instruction and the line in the file*/
+ instruction->decision = 1;
+ instruction->decision_branch1_position = jmp_position1;
+ instruction->decision_branch2_position = jmp_position2;
+ line->decision = 1;
+ line->decision_branch1_lineno = decision->branch1_lineno;
+ line->decision_branch2_lineno = decision->branch2_lineno;
+ }
+
+ xdfree(sline);
+ xdfree(sposition);
 }
 
 static zend_brk_cont_element* xdebug_find_brk_cont(zval *nest_levels_zval, int array_offset, zend_op_array *op_array)
@@ -227,11 +442,15 @@
 
 static void prefill_from_oparray(char *fn, zend_op_array *opa TSRMLS_DC)
 {
- unsigned int i;
+ char cache_key[256];
+ int cache_key_len;
+ void *dummy;
+ unsigned int i, j = 0;
         xdebug_set *set = NULL;
-
+ fprintf(stderr, "XDEBUG_CC: prefill_from_oparray: starting...\n");
         opa->reserved[XG(reserved_offset)] = (void*) 1;
 
+#ifdef ZEND_ENGINE_2
         /* Check for abstract methods and simply return from this function in those
          * cases. */
 #if PHP_VERSION_ID >= 50300
@@ -242,8 +461,10 @@
         {
                 return;
         }
+#endif
 
         /* Run dead code analysis if requested */
+
         if (XG(code_coverage_dead_code_analysis) && opa->done_pass_two) {
                 set = xdebug_set_create(opa->last);
                 xdebug_analyse_branch(opa, 0, set);
@@ -252,7 +473,8 @@
         /* The normal loop then finally */
         for (i = 0; i < opa->last; i++) {
                 zend_op opcode = opa->opcodes[i];
- prefill_from_opcode(fn, opcode, set ? !xdebug_set_in(set, i) : 0 TSRMLS_CC);
+ fprintf(stderr, "XDEBUG_CC: prefill_from_oparray: calling prefill_from_opcode for opcode %d.\n", i);
+ prefill_from_opcode(fn, opa, opcode, i, &j, set ? !xdebug_set_in(set, i) : 0 TSRMLS_CC);
         }
 
         if (set) {
@@ -357,38 +579,92 @@
         }
 }
 
+static void add_instruction(void *ret, xdebug_hash_element *e)
+{
+ xdebug_coverage_instruction *instruction = (xdebug_coverage_instruction*) e->ptr;
+ zval *retval = (zval*) ret;
+ zval *instruction_zval;
+
+ TSRMLS_FETCH();
+ MAKE_STD_ZVAL(instruction_zval);
+ array_init(instruction_zval);
+
+ add_assoc_long(instruction_zval, "position", instruction->position);
+ add_assoc_long(instruction_zval, "count", instruction->count);
+ add_assoc_string(instruction_zval, "opcode name", instruction->opcode_name, 1);
+ add_assoc_long(instruction_zval, "dead", instruction->dead);
+ add_assoc_long(instruction_zval, "executed", instruction->executed);
+ add_index_zval(retval, instruction->position, instruction_zval);
+}
 
 static void add_line(void *ret, xdebug_hash_element *e)
 {
         xdebug_coverage_line *line = (xdebug_coverage_line*) e->ptr;
         zval *retval = (zval*) ret;
-
- if (line->executable && (line->count == 0)) {
- add_index_long(retval, line->lineno, -line->executable);
- } else {
- add_index_long(retval, line->lineno, 1);
- }
+ zval *instructions;
+ zval *line_zval;
+ HashTable *target_hash;
+
+ TSRMLS_FETCH();
+ MAKE_STD_ZVAL(line_zval);
+ MAKE_STD_ZVAL(instructions);
+ array_init(line_zval);
+ array_init(instructions);
+
+ xdebug_hash_apply(line->instructions, (void *) instructions, add_instruction);
+ /* Sort instructions on position */
+ target_hash = HASH_OF(instructions);
+ zend_hash_sort(target_hash, zend_qsort, xdebug_lineno_cmp, 0 TSRMLS_CC);
+ add_assoc_long(line_zval, "lineno", line->lineno);
+ add_assoc_long(line_zval, "count", line->count);
+ add_assoc_long(line_zval, "executed", line->executed);
+
+
+ add_assoc_zval(line_zval, "instructions", instructions);
+ add_index_zval(retval, line->lineno, line_zval);
 }
 
-static void add_file(void *ret, xdebug_hash_element *e)
+static void add_cc_function(void *ret, xdebug_hash_element *e)
 {
- xdebug_coverage_file *file = (xdebug_coverage_file*) e->ptr;
+ xdebug_coverage_function *function = (xdebug_coverage_function*) e->ptr;
         zval *retval = (zval*) ret;
         zval *lines;
- HashTable *target_hash;
+ zval *function_zval;
         TSRMLS_FETCH();
+ HashTable *target_hash;
 
         MAKE_STD_ZVAL(lines);
         array_init(lines);
+ MAKE_STD_ZVAL(function_zval);
+ array_init(function_zval);
 
- /* Add all the lines */
- xdebug_hash_apply(file->lines, (void *) lines, add_line);
+
+ xdebug_hash_apply(function->lines, (void *) lines, add_line);
 
         /* Sort on linenumber */
         target_hash = HASH_OF(lines);
         zend_hash_sort(target_hash, zend_qsort, xdebug_lineno_cmp, 0 TSRMLS_CC);
 
- add_assoc_zval_ex(retval, file->name, strlen(file->name) + 1, lines);
+ /* Add all the lines */
+ add_assoc_zval_ex(function_zval, "lines", strlen("lines") + 1, lines);
+ add_assoc_long(function_zval, "total_branches", function->total_branches);
+ add_assoc_long(function_zval, "branches_counted", function->branches_counted);
+ add_assoc_zval_ex(retval, function->name, strlen(function->name) + 1, function_zval);
+}
+
+static void add_file(void *ret, xdebug_hash_element *e)
+{
+ xdebug_coverage_file *file = (xdebug_coverage_file*) e->ptr;
+ zval *retval = (zval*) ret;
+ zval *functions;
+ TSRMLS_FETCH();
+
+ MAKE_STD_ZVAL(functions);
+ array_init(functions);
+
+ /* Add all the functions */
+ xdebug_hash_apply(file->functions, (void *) functions, add_cc_function);
+ add_assoc_zval_ex(retval, file->name, strlen(file->name) + 1, functions);
 }
 
 PHP_FUNCTION(xdebug_get_code_coverage)
Received on Fri Jul 17 2009 - 08:20:59 BST

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