package.xml 0000644 0001750 0001750 00000020466 14724013203 011651 0 ustar remi remi
pcov
pecl.php.net
Code coverage driver
A self contained php-code-coverage compatible driver for PHP.
Joe Watkins
krakjoe
krakjoe@php.net
yes
Remi Collet
remi
remi@php.net
yes
2024-12-04
1.0.12
1.0.0
stable
stable
PHP License
- PHP 8.4 compatibility
7.1.0
1.10
pcov
2021-12-20
1.0.11
1.0.0
stable
stable
PHP License
- Fix anonymous function coverage
2021-11-24
1.0.10
1.0.0
stable
stable
PHP License
- Use zend_new_interned_string instead of hand-rolling
- Use ZEND_MOD_END to fix build warning
2021-06-07
1.0.9
1.0.0
stable
stable
PHP License
- Fix #67 huge memory consumption to generate coverage
2021-03-22
1.0.8
1.0.0
stable
stable
PHP License
- Fix Windows build
2021-03-19
1.0.7
1.0.0
stable
stable
PHP License
- Fix #46 Segmentation fault on 7.4 on macOS with anonymous class
- Fix #59 "malloc_consolidate(): invalid chunk size" with PHP 8.1
2019-06-13
1.0.6
1.0.0
stable
stable
PHP License
- Fix for 7.4.0alpha1
2019-06-11
1.0.5
1.0.0
stable
stable
PHP License
- Fix #19 fault in 7.2 caused by modification of filename pointer
2019-06-03
1.0.4
1.0.0
stable
stable
PHP License
- Fix #17 Lines not covered when more tests are run
- Improve perf of clear routine
- Merge upstream cfg updates (switch block change)
- Omit ignored opcodes from internal coverage data completely (doesn't effect users, except less memory used)
2019-05-06
1.0.3
1.0.0
stable
stable
PHP License
- Fix #12 issue with faulty line coverage
2019-03-31
1.0.2
1.0.0
stable
stable
PHP License
- Fix #10 second non-cli SAPI request behaves strangely
2019-03-25
1.0.1
1.0.0
stable
stable
PHP License
- Release memory from building CFG as early as possible
- Compat with 7.4/8.0 changes
2019-01-30
1.0.0
1.0.0
stable
stable
PHP License
- initial pecl stable release
2019-01-22
0.9.0
0.9.0
beta
beta
PHP License
- initial pecl release
pcov-1.0.12/tests/001.phpt 0000664 0001750 0001750 00000000711 14724013203 013550 0 ustar remi remi --TEST--
start/stop
--SKIPIF--
--INI--
pcov.enabled = 1
--FILE--
--EXPECTF--
array(1) {
["%s%e001.php"]=>
array(7) {
[2]=>
int(-1)
[3]=>
int(1)
[4]=>
int(1)
[5]=>
int(1)
[7]=>
int(1)
[9]=>
int(-1)
[11]=>
int(-1)
}
}
pcov-1.0.12/tests/002.phpt 0000664 0001750 0001750 00000001054 14724013203 013552 0 ustar remi remi --TEST--
clear
--SKIPIF--
--INI--
pcov.enabled = 1
--FILE--
--EXPECTF--
array(1) {
["%s%e002.php"]=>
array(9) {
[2]=>
int(-1)
[3]=>
int(1)
[4]=>
int(1)
[5]=>
int(1)
[7]=>
int(1)
[9]=>
int(-1)
[11]=>
int(-1)
[13]=>
int(-1)
[15]=>
int(-1)
}
}
array(0) {
}
pcov-1.0.12/tests/003.phpt 0000664 0001750 0001750 00000000574 14724013203 013561 0 ustar remi remi --TEST--
\pcov\collect non array filter
--SKIPIF--
--INI--
pcov.enabled = 0
--FILE--
--EXPECTF--
Fatal error: Uncaught TypeError: %s type array, string given in %s%e003.php:2
Stack trace:
#0 %s%e003.php(2): pcov\collect(1, '')
#1 {main}
thrown in %s%e003.php on line 2
pcov-1.0.12/tests/004.phpt 0000664 0001750 0001750 00000000623 14724013203 013555 0 ustar remi remi --TEST--
\pcov\collect not recognized filter type
--SKIPIF--
--INI--
pcov.enabled = 1
--FILE--
--EXPECTF--
Fatal error: Uncaught TypeError: type must be \pcov\inclusive, \pcov\exclusive, or \pcov\all in %s%e004.php:2
Stack trace:
#0 %s%e004.php(2): pcov\collect(42)
#1 {main}
thrown in %s%e004.php on line 2
pcov-1.0.12/tests/005.phpt 0000664 0001750 0001750 00000000574 14724013203 013563 0 ustar remi remi --TEST--
anonymous class
--SKIPIF--
--INI--
pcov.enabled = 1
--FILE--
--EXPECTF--
array(1) {
["%s%e005.php"]=>
array(5) {
[2]=>
int(-1)
[3]=>
int(1)
[4]=>
int(1)
[5]=>
int(-1)
[7]=>
int(-1)
}
}
pcov-1.0.12/tests/006.phpt 0000664 0001750 0001750 00000000472 14724013203 013561 0 ustar remi remi --TEST--
phpinfo
--SKIPIF--
--INI--
pcov.enabled = 0
--FILE--
--EXPECTF--
%A
PCOV support => Disabled
PCOV version => %s
pcov.directory => auto
pcov.exclude => none
pcov.initial.memory => 65336 bytes
pcov.initial.files => 64
%A
pcov-1.0.12/tests/007.phpt 0000664 0001750 0001750 00000000771 14724013203 013564 0 ustar remi remi --TEST--
anonymous function
--SKIPIF--
--INI--
pcov.enabled = 1
--FILE--
--EXPECTF--
array(1) {
["%s%e007.php"]=>
array(7) {
[2]=>
int(-1)
[3]=>
int(1)
[6]=>
int(1)
[7]=>
int(1)
[8]=>
int(-1)
[10]=>
int(-1)
[4]=>
int(1)
}
}
pcov-1.0.12/cfg/701/zend_cfg.h 0000664 0001750 0001750 00000012572 14724013203 014217 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_CFG_H
#define ZEND_CFG_H
/* zend_basic_bloc.flags */
#define ZEND_BB_START (1<<0) /* fist block */
#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
#define ZEND_BB_TARGET (1<<2) /* jump taget */
#define ZEND_BB_EXIT (1<<3) /* without successors */
#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
#define ZEND_BB_TRY (1<<5) /* start of try block */
#define ZEND_BB_CATCH (1<<6) /* start of catch block */
#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
#define ZEND_BB_GEN_VAR (1<<9) /* start of live range */
#define ZEND_BB_KILL_VAR (1<<10) /* end of live range */
#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
#define ZEND_BB_LOOP_HEADER (1<<16)
#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
#define ZEND_BB_REACHABLE (1<<31)
#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR)
typedef struct _zend_basic_block {
uint32_t flags;
uint32_t start; /* first opcode number */
uint32_t len; /* number of opcodes */
int successors[2]; /* up to 2 successor blocks */
int predecessors_count; /* number of predecessors */
int predecessor_offset; /* offset of 1-st predecessor */
int idom; /* immediate dominator block */
int loop_header; /* closest loop header, or -1 */
int level; /* steps away from the entry in the dom. tree */
int children; /* list of dominated blocks */
int next_child; /* next dominated block */
} zend_basic_block;
/*
+------------+---+---+---+---+---+
| |OP1|OP2|EXT| 0 | 1 |
+------------+---+---+---+---+---+
|JMP |ADR| | |OP1| - |
|JMPZ | |ADR| |OP2|FOL|
|JMPNZ | |ADR| |OP2|FOL|
|JMPZNZ | |ADR|ADR|OP2|EXT|
|JMPZ_EX | |ADR| |OP2|FOL|
|JMPNZ_EX | |ADR| |OP2|FOL|
|JMP_SET | |ADR| |OP2|FOL|
|COALESCE | |ADR| |OP2|FOL|
|ASSERT_CHK | |ADR| |OP2|FOL|
|NEW | |ADR| |OP2|FOL|
|DCL_ANON* |ADR| | |OP1|FOL|
|FE_RESET_* | |ADR| |OP2|FOL|
|FE_FETCH_* | | |ADR|EXT|FOL|
|CATCH | | |ADR|EXT|FOL|
|FAST_CALL |ADR| | |OP1|FOL|
|FAST_RET | | | | - | - |
|RETURN* | | | | - | - |
|EXIT | | | | - | - |
|THROW | | | | - | - |
|* | | | |FOL| - |
+------------+---+---+---+---+---+
*/
typedef struct _zend_cfg {
int blocks_count; /* number of basic blocks */
zend_basic_block *blocks; /* array of basic blocks */
int *predecessors;
uint32_t *map;
unsigned int split_at_live_ranges : 1;
unsigned int split_at_calls : 1;
unsigned int split_at_recv : 1;
} zend_cfg;
/* Build Flags */
#define ZEND_RT_CONSTANTS (1<<31)
#define ZEND_CFG_STACKLESS (1<<30)
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
#define ZEND_SSA_RC_INFERENCE (1<<27)
#define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
#define ZEND_CFG_RECV_ENTRY (1<<24)
#define ZEND_CALL_TREE (1<<23)
#define CRT_CONSTANT_EX(op_array, node, rt_constants) \
((rt_constants) ? \
RT_CONSTANT(op_array, (node)) \
: \
CT_CONSTANT_EX(op_array, (node).constant) \
)
#define CRT_CONSTANT(node) \
CRT_CONSTANT_EX(op_array, node, (build_flags & ZEND_RT_CONSTANTS))
#define RETURN_VALUE_USED(opline) \
((opline)->result_type != IS_UNUSED)
BEGIN_EXTERN_C()
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
END_EXTERN_C()
#endif /* ZEND_CFG_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/701/zend_cfg.c 0000664 0001750 0001750 00000045270 14724013203 014213 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov |
+----------------------------------------------------------------------+
This source file has been adapted for pcov so that the CFG from O+ is standalone
*/
#include "php.h"
#include "zend_compile.h"
#include "zend_cfg.h"
#include "zend_worklist.h"
/* func flags */
#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
#define ZEND_FUNC_HAS_CALLS (1<<1)
#define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
#define ZEND_FUNC_NO_LOOPS (1<<3)
#define ZEND_FUNC_IRREDUCIBLE (1<<4)
#define ZEND_FUNC_RECURSIVE (1<<7)
#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
#define ZEND_FUNC_HAS_EXTENDED_INFO (1<<10)
/* The following flags are valid only for return values of internal functions
* returned by zend_get_func_info()
*/
#define FUNC_MAY_WARN (1<<30)
typedef struct _zend_func_info zend_func_info;
typedef struct _zend_call_info zend_call_info;
#define ZEND_FUNC_INFO(op_array) \
((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
#define ZEND_SET_FUNC_INFO(op_array, info) do { \
zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
*pinfo = info; \
} while (0)
static uint32_t zend_cfg_classify_function(zend_string *name, uint32_t num_args) {
if (zend_string_equals_literal(name, "extract")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "compact")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "get_defined_vars")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "assert")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "func_num_args")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_arg")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_args")) {
return ZEND_FUNC_VARARG;
} else {
return 0;
}
}
static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
{
zend_uchar opcode;
zend_basic_block *b0;
int successor_0, successor_1;
zend_basic_block *blocks = cfg->blocks;
while (1) {
b->flags |= ZEND_BB_REACHABLE;
successor_0 = b->successors[0];
if (successor_0 >= 0) {
successor_1 = b->successors[1];
if (successor_1 >= 0) {
b0 = blocks + successor_0;
b0->flags |= ZEND_BB_TARGET;
if (!(b0->flags & ZEND_BB_REACHABLE)) {
zend_mark_reachable(opcodes, cfg, b0);
}
ZEND_ASSERT(b->len != 0);
opcode = opcodes[b->start + b->len - 1].opcode;
b = blocks + successor_1;
if (opcode == ZEND_JMPZNZ) {
b->flags |= ZEND_BB_TARGET;
} else {
b->flags |= ZEND_BB_FOLLOW;
}
} else if (b->len != 0) {
opcode = opcodes[b->start + b->len - 1].opcode;
b = blocks + successor_0;
if (opcode == ZEND_JMP) {
b->flags |= ZEND_BB_TARGET;
} else {
b->flags |= ZEND_BB_FOLLOW;
if (cfg->split_at_calls) {
if (opcode == ZEND_INCLUDE_OR_EVAL ||
opcode == ZEND_GENERATOR_CREATE ||
opcode == ZEND_YIELD ||
opcode == ZEND_YIELD_FROM ||
opcode == ZEND_DO_FCALL ||
opcode == ZEND_DO_UCALL ||
opcode == ZEND_DO_FCALL_BY_NAME) {
b->flags |= ZEND_BB_ENTRY;
}
}
if (cfg->split_at_recv) {
if (opcode == ZEND_RECV ||
opcode == ZEND_RECV_INIT) {
b->flags |= ZEND_BB_RECV_ENTRY;
}
}
}
} else {
b = blocks + successor_0;
b->flags |= ZEND_BB_FOLLOW;
}
if (b->flags & ZEND_BB_REACHABLE) return;
} else {
b->flags |= ZEND_BB_EXIT;
return;
}
}
}
/* }}} */
static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
blocks[start].flags = ZEND_BB_START;
zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
if (op_array->last_live_range || op_array->last_try_catch) {
zend_basic_block *b;
int j, changed;
uint32_t *block_map = cfg->map;
do {
changed = 0;
/* Add live range paths */
for (j = 0; j < op_array->last_live_range; j++) {
zend_live_range *live_range = &op_array->live_range[j];
if (live_range->var == (uint32_t)-1) {
/* this live range already removed */
continue;
}
b = blocks + block_map[live_range->start];
if (b->flags & ZEND_BB_REACHABLE) {
while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
/* check if NOP breaks incorrect smart branch */
if (b->len == 2
&& (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
|| op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
&& (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
&& b->start > 0
&& zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
break;
}
b->start++;
b->len--;
}
if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) {
/* mark as removed (empty live range) */
live_range->var = (uint32_t)-1;
continue;
}
b->flags |= ZEND_BB_GEN_VAR;
b = blocks + block_map[live_range->end];
b->flags |= ZEND_BB_KILL_VAR;
if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) {
if (cfg->split_at_live_ranges) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
} else {
ZEND_ASSERT(b->start == live_range->end);
b->flags |= ZEND_BB_UNREACHABLE_FREE;
}
}
} else {
ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
}
}
/* Add exception paths */
for (j = 0; j < op_array->last_try_catch; j++) {
/* check for jumps into the middle of try block */
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (!(b->flags & ZEND_BB_REACHABLE)) {
zend_basic_block *end;
if (op_array->try_catch_array[j].catch_op) {
end = blocks + block_map[op_array->try_catch_array[j].catch_op];
while (b != end) {
if (b->flags & ZEND_BB_REACHABLE) {
op_array->try_catch_array[j].try_op = b->start;
break;
}
b++;
}
}
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (!(b->flags & ZEND_BB_REACHABLE)) {
if (op_array->try_catch_array[j].finally_op) {
end = blocks + block_map[op_array->try_catch_array[j].finally_op];
while (b != end) {
if (b->flags & ZEND_BB_REACHABLE) {
op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
break;
}
b++;
}
}
}
}
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (b->flags & ZEND_BB_REACHABLE) {
b->flags |= ZEND_BB_TRY;
if (op_array->try_catch_array[j].catch_op) {
b = blocks + block_map[op_array->try_catch_array[j].catch_op];
b->flags |= ZEND_BB_CATCH;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
if (op_array->try_catch_array[j].finally_op) {
b = blocks + block_map[op_array->try_catch_array[j].finally_op];
b->flags |= ZEND_BB_FINALLY;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
if (op_array->try_catch_array[j].finally_end) {
b = blocks + block_map[op_array->try_catch_array[j].finally_end];
b->flags |= ZEND_BB_FINALLY_END;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
} else {
if (op_array->try_catch_array[j].catch_op) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
}
if (op_array->try_catch_array[j].finally_op) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
}
if (op_array->try_catch_array[j].finally_end) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
}
}
}
} while (changed);
}
}
/* }}} */
static void record_successor(zend_basic_block *blocks, int pred, int n, int succ)
{
blocks[pred].successors[n] = succ;
}
static void initialize_block(zend_basic_block *block) {
block->flags = 0;
block->successors[0] = -1;
block->successors[1] = -1;
block->predecessors_count = 0;
block->predecessor_offset = -1;
block->idom = -1;
block->loop_header = -1;
block->level = -1;
block->children = -1;
block->next_child = -1;
}
#define BB_START(i) do { \
if (!block_map[i]) { blocks_count++;} \
block_map[i]++; \
} while (0)
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
{
uint32_t flags = 0;
uint32_t i;
int j;
uint32_t *block_map;
zend_function *fn;
int blocks_count = 0;
zend_basic_block *blocks;
zval *zv;
zend_bool extra_entry_block = 0;
cfg->split_at_live_ranges = (build_flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES) != 0;
cfg->split_at_calls = (build_flags & ZEND_CFG_STACKLESS) != 0;
cfg->split_at_recv = (build_flags & ZEND_CFG_RECV_ENTRY) != 0 && (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0;
cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
if (!block_map) {
return FAILURE;
}
/* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
BB_START(0);
for (i = 0; i < op_array->last; i++) {
zend_op *opline = op_array->opcodes + i;
switch(opline->opcode) {
case ZEND_RECV:
case ZEND_RECV_INIT:
if (build_flags & ZEND_CFG_RECV_ENTRY) {
BB_START(i + 1);
}
break;
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_INCLUDE_OR_EVAL:
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
case ZEND_GENERATOR_CREATE:
case ZEND_YIELD:
case ZEND_YIELD_FROM:
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
case ZEND_DO_FCALL:
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
flags |= ZEND_FUNC_HAS_CALLS;
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
case ZEND_DO_ICALL:
flags |= ZEND_FUNC_HAS_CALLS;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_NS_FCALL_BY_NAME:
zv = CRT_CONSTANT(opline->op2);
if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
/* The third literal is the lowercased unqualified name */
zv += 2;
}
if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
if (fn->type == ZEND_INTERNAL_FUNCTION) {
flags |= zend_cfg_classify_function(
Z_STR_P(zv), opline->extended_value);
}
}
break;
case ZEND_FAST_CALL:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_FAST_RET:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMP:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMPZNZ:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_CATCH:
if (!opline->result.num) {
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
}
BB_START(i + 1);
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
BB_START(i + 1);
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_UNSET_VAR:
case ZEND_ISSET_ISEMPTY_VAR:
if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) &&
!(opline->extended_value & ZEND_QUICK_SET)) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
(opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
!op_array->function_name) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
}
break;
case ZEND_FETCH_R:
case ZEND_FETCH_W:
case ZEND_FETCH_RW:
case ZEND_FETCH_FUNC_ARG:
case ZEND_FETCH_IS:
case ZEND_FETCH_UNSET:
if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
(opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
!op_array->function_name) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
}
break;
}
}
/* If the entry block has predecessors, we may need to split it */
if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
&& op_array->last > 0 && block_map[0] > 1) {
extra_entry_block = 1;
}
if (cfg->split_at_live_ranges) {
for (j = 0; j < op_array->last_live_range; j++) {
BB_START(op_array->live_range[j].start);
BB_START(op_array->live_range[j].end);
}
}
if (op_array->last_try_catch) {
for (j = 0; j < op_array->last_try_catch; j++) {
BB_START(op_array->try_catch_array[j].try_op);
if (op_array->try_catch_array[j].catch_op) {
BB_START(op_array->try_catch_array[j].catch_op);
}
if (op_array->try_catch_array[j].finally_op) {
BB_START(op_array->try_catch_array[j].finally_op);
}
if (op_array->try_catch_array[j].finally_end) {
BB_START(op_array->try_catch_array[j].finally_end);
}
}
}
blocks_count += extra_entry_block;
cfg->blocks_count = blocks_count;
/* Build CFG, Step 2: Build Array of Basic Blocks */
cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
if (!blocks) {
return FAILURE;
}
blocks_count = -1;
if (extra_entry_block) {
initialize_block(&blocks[0]);
blocks[0].start = 0;
blocks[0].len = 0;
blocks_count++;
}
for (i = 0; i < op_array->last; i++) {
if (block_map[i]) {
if (blocks_count >= 0) {
blocks[blocks_count].len = i - blocks[blocks_count].start;
}
blocks_count++;
initialize_block(&blocks[blocks_count]);
blocks[blocks_count].start = i;
}
block_map[i] = blocks_count;
}
blocks[blocks_count].len = i - blocks[blocks_count].start;
blocks_count++;
/* Build CFG, Step 3: Calculate successors */
for (j = 0; j < blocks_count; j++) {
zend_op *opline;
if (blocks[j].len == 0) {
record_successor(blocks, j, 0, j + 1);
continue;
}
opline = op_array->opcodes + blocks[j].start + blocks[j].len - 1;
switch (opline->opcode) {
case ZEND_FAST_RET:
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
break;
case ZEND_JMP:
record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
break;
case ZEND_JMPZNZ:
record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
record_successor(blocks, j, 1, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
record_successor(blocks, j, 1, j + 1);
break;
case ZEND_CATCH:
if (!opline->result.num) {
record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
record_successor(blocks, j, 1, j + 1);
} else {
record_successor(blocks, j, 0, j + 1);
}
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
record_successor(blocks, j, 0, block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)]);
record_successor(blocks, j, 1, j + 1);
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes]);
record_successor(blocks, j, 1, j + 1);
break;
case ZEND_FAST_CALL:
record_successor(blocks, j, 0, block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes]);
record_successor(blocks, j, 1, j + 1);
break;
default:
record_successor(blocks, j, 0, j + 1);
break;
}
}
/* Build CFG, Step 4, Mark Reachable Basic Blocks */
zend_mark_reachable_blocks(op_array, cfg, 0);
return SUCCESS;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/701/zend_worklist.h 0000664 0001750 0001750 00000010021 14724013203 015321 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andy Wingo |
+----------------------------------------------------------------------+
*/
/* $Id:$ */
#ifndef _ZEND_WORKLIST_H_
#define _ZEND_WORKLIST_H_
#include "zend_arena.h"
#include "zend_bitset.h"
typedef struct _zend_worklist_stack {
int *buf;
int len;
int capacity;
} zend_worklist_stack;
#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
(s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
(s)->len = 0; \
(s)->capacity = _len; \
} while (0)
#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
free_alloca((s)->buf, use_heap)
static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
{
ZEND_ASSERT(len >= 0);
stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
if (!stack->buf) {
return FAILURE;
}
stack->len = 0;
stack->capacity = len;
return SUCCESS;
}
static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
{
ZEND_ASSERT(stack->len < stack->capacity);
stack->buf[stack->len++] = i;
}
static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
{
ZEND_ASSERT(stack->len);
return stack->buf[stack->len - 1];
}
static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
{
ZEND_ASSERT(stack->len);
return stack->buf[--stack->len];
}
typedef struct _zend_worklist {
zend_bitset visited;
zend_worklist_stack stack;
} zend_worklist;
#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
(w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
(w)->stack.len = 0; \
(w)->stack.capacity = _len; \
(w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
} while (0)
#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
free_alloca((w)->stack.buf, use_heap)
static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
{
ZEND_ASSERT(len >= 0);
worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
if (!worklist->visited) {
return FAILURE;
}
return zend_worklist_stack_prepare(arena, &worklist->stack, len);
}
static inline int zend_worklist_len(zend_worklist *worklist)
{
return worklist->stack.len;
}
static inline int zend_worklist_push(zend_worklist *worklist, int i)
{
ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
if (zend_bitset_in(worklist->visited, i)) {
return 0;
}
zend_bitset_incl(worklist->visited, i);
zend_worklist_stack_push(&worklist->stack, i);
return 1;
}
static inline int zend_worklist_peek(zend_worklist *worklist)
{
return zend_worklist_stack_peek(&worklist->stack);
}
static inline int zend_worklist_pop(zend_worklist *worklist)
{
/* Does not clear visited flag */
return zend_worklist_stack_pop(&worklist->stack);
}
#endif /* _ZEND_WORKLIST_H_ */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/702/zend_cfg.h 0000664 0001750 0001750 00000013426 14724013203 014217 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_CFG_H
#define ZEND_CFG_H
/* zend_basic_bloc.flags */
#define ZEND_BB_START (1<<0) /* fist block */
#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
#define ZEND_BB_TARGET (1<<2) /* jump taget */
#define ZEND_BB_EXIT (1<<3) /* without successors */
#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
#define ZEND_BB_TRY (1<<5) /* start of try block */
#define ZEND_BB_CATCH (1<<6) /* start of catch block */
#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
#define ZEND_BB_GEN_VAR (1<<9) /* start of live range */
#define ZEND_BB_KILL_VAR (1<<10) /* end of live range */
#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
#define ZEND_BB_LOOP_HEADER (1<<16)
#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
#define ZEND_BB_REACHABLE (1<<31)
#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR)
typedef struct _zend_basic_block {
int *successors; /* successor block indices */
uint32_t flags;
uint32_t start; /* first opcode number */
uint32_t len; /* number of opcodes */
int successors_count; /* number of successors */
int predecessors_count; /* number of predecessors */
int predecessor_offset; /* offset of 1-st predecessor */
int idom; /* immediate dominator block */
int loop_header; /* closest loop header, or -1 */
int level; /* steps away from the entry in the dom. tree */
int children; /* list of dominated blocks */
int next_child; /* next dominated block */
int successors_storage[2]; /* up to 2 successor blocks */
} zend_basic_block;
/*
+------------+---+---+---+---+---+
| |OP1|OP2|EXT| 0 | 1 |
+------------+---+---+---+---+---+
|JMP |ADR| | |OP1| - |
|JMPZ | |ADR| |OP2|FOL|
|JMPNZ | |ADR| |OP2|FOL|
|JMPZNZ | |ADR|ADR|OP2|EXT|
|JMPZ_EX | |ADR| |OP2|FOL|
|JMPNZ_EX | |ADR| |OP2|FOL|
|JMP_SET | |ADR| |OP2|FOL|
|COALESCE | |ADR| |OP2|FOL|
|ASSERT_CHK | |ADR| |OP2|FOL|
|NEW | |ADR| |OP2|FOL|
|DCL_ANON* |ADR| | |OP1|FOL|
|FE_RESET_* | |ADR| |OP2|FOL|
|FE_FETCH_* | | |ADR|EXT|FOL|
|CATCH | | |ADR|EXT|FOL|
|FAST_CALL |ADR| | |OP1|FOL|
|FAST_RET | | | | - | - |
|RETURN* | | | | - | - |
|EXIT | | | | - | - |
|THROW | | | | - | - |
|* | | | |FOL| - |
+------------+---+---+---+---+---+
*/
typedef struct _zend_cfg {
int blocks_count; /* number of basic blocks */
int edges_count; /* number of edges */
zend_basic_block *blocks; /* array of basic blocks */
int *predecessors;
uint32_t *map;
unsigned int split_at_live_ranges : 1;
unsigned int split_at_calls : 1;
unsigned int split_at_recv : 1;
unsigned int dynamic : 1; /* accesses varables by name */
unsigned int vararg : 1; /* uses func_get_args() */
} zend_cfg;
/* Build Flags */
#define ZEND_RT_CONSTANTS (1<<31)
#define ZEND_CFG_STACKLESS (1<<30)
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
#define ZEND_SSA_RC_INFERENCE (1<<27)
#define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
#define ZEND_CFG_RECV_ENTRY (1<<24)
#define ZEND_CALL_TREE (1<<23)
#define ZEND_SSA_USE_CV_RESULTS (1<<22)
#define CRT_CONSTANT_EX(op_array, node, rt_constants) \
((rt_constants) ? \
RT_CONSTANT(op_array, (node)) \
: \
CT_CONSTANT_EX(op_array, (node).constant) \
)
#define CRT_CONSTANT(node) \
CRT_CONSTANT_EX(op_array, node, (build_flags & ZEND_RT_CONSTANTS))
#define RETURN_VALUE_USED(opline) \
((opline)->result_type != IS_UNUSED)
BEGIN_EXTERN_C()
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
END_EXTERN_C()
#endif /* ZEND_CFG_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/702/zend_cfg.c 0000664 0001750 0001750 00000050117 14724013203 014210 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov |
+----------------------------------------------------------------------+
This source file has been adapted for pcov so that the CFG from O+ is standalone
*/
#include "php.h"
#include "zend_compile.h"
#include "zend_cfg.h"
#include "zend_worklist.h"
/* func flags */
#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
#define ZEND_FUNC_HAS_CALLS (1<<1)
#define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
#define ZEND_FUNC_NO_LOOPS (1<<3)
#define ZEND_FUNC_IRREDUCIBLE (1<<4)
#define ZEND_FUNC_RECURSIVE (1<<7)
#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
#define ZEND_FUNC_HAS_EXTENDED_INFO (1<<10)
/* The following flags are valid only for return values of internal functions
* returned by zend_get_func_info()
*/
#define FUNC_MAY_WARN (1<<30)
typedef struct _zend_func_info zend_func_info;
typedef struct _zend_call_info zend_call_info;
#define ZEND_FUNC_INFO(op_array) \
((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
#define ZEND_SET_FUNC_INFO(op_array, info) do { \
zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
*pinfo = info; \
} while (0)
static uint32_t zend_cfg_classify_function(zend_string *name, uint32_t num_args) {
if (zend_string_equals_literal(name, "extract")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "compact")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "get_defined_vars")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "assert")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "func_num_args")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_arg")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_args")) {
return ZEND_FUNC_VARARG;
} else {
return 0;
}
}
static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
while (1) {
int i;
b->flags |= ZEND_BB_REACHABLE;
if (b->successors_count == 0) {
b->flags |= ZEND_BB_EXIT;
return;
}
for (i = 0; i < b->successors_count; i++) {
zend_basic_block *succ = blocks + b->successors[i];
if (b->len != 0) {
zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
if (b->successors_count == 1) {
if (opcode == ZEND_JMP) {
succ->flags |= ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_FOLLOW;
if (cfg->split_at_calls) {
if (opcode == ZEND_INCLUDE_OR_EVAL ||
opcode == ZEND_GENERATOR_CREATE ||
opcode == ZEND_YIELD ||
opcode == ZEND_YIELD_FROM ||
opcode == ZEND_DO_FCALL ||
opcode == ZEND_DO_UCALL ||
opcode == ZEND_DO_FCALL_BY_NAME) {
succ->flags |= ZEND_BB_ENTRY;
}
}
if (cfg->split_at_recv) {
if (opcode == ZEND_RECV ||
opcode == ZEND_RECV_INIT) {
succ->flags |= ZEND_BB_RECV_ENTRY;
}
}
}
} else if (b->successors_count == 2) {
if (i == 0 || opcode == ZEND_JMPZNZ) {
succ->flags |= ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_FOLLOW;
}
} else {
ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
if (i == b->successors_count - 1) {
succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_TARGET;
}
}
} else {
succ->flags |= ZEND_BB_FOLLOW;
}
if (i == b->successors_count - 1) {
/* Tail call optimization */
if (succ->flags & ZEND_BB_REACHABLE) {
return;
}
b = succ;
break;
} else {
/* Recusively check reachability */
if (!(succ->flags & ZEND_BB_REACHABLE)) {
zend_mark_reachable(opcodes, cfg, succ);
}
}
}
}
}
/* }}} */
static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
blocks[start].flags = ZEND_BB_START;
zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
if (op_array->last_live_range || op_array->last_try_catch) {
zend_basic_block *b;
int j, changed;
uint32_t *block_map = cfg->map;
do {
changed = 0;
/* Add live range paths */
for (j = 0; j < op_array->last_live_range; j++) {
zend_live_range *live_range = &op_array->live_range[j];
if (live_range->var == (uint32_t)-1) {
/* this live range already removed */
continue;
}
b = blocks + block_map[live_range->start];
if (b->flags & ZEND_BB_REACHABLE) {
while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
/* check if NOP breaks incorrect smart branch */
if (b->len == 2
&& (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
|| op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
&& (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
&& b->start > 0
&& zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
break;
}
b->start++;
b->len--;
}
if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) {
/* mark as removed (empty live range) */
live_range->var = (uint32_t)-1;
continue;
}
b->flags |= ZEND_BB_GEN_VAR;
b = blocks + block_map[live_range->end];
b->flags |= ZEND_BB_KILL_VAR;
if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) {
if (cfg->split_at_live_ranges) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
} else {
b->flags |= ZEND_BB_UNREACHABLE_FREE;
}
}
} else {
ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
}
}
/* Add exception paths */
for (j = 0; j < op_array->last_try_catch; j++) {
/* check for jumps into the middle of try block */
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (!(b->flags & ZEND_BB_REACHABLE)) {
zend_basic_block *end;
if (op_array->try_catch_array[j].catch_op) {
end = blocks + block_map[op_array->try_catch_array[j].catch_op];
while (b != end) {
if (b->flags & ZEND_BB_REACHABLE) {
op_array->try_catch_array[j].try_op = b->start;
break;
}
b++;
}
}
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (!(b->flags & ZEND_BB_REACHABLE)) {
if (op_array->try_catch_array[j].finally_op) {
end = blocks + block_map[op_array->try_catch_array[j].finally_op];
while (b != end) {
if (b->flags & ZEND_BB_REACHABLE) {
op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
break;
}
b++;
}
}
}
}
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (b->flags & ZEND_BB_REACHABLE) {
b->flags |= ZEND_BB_TRY;
if (op_array->try_catch_array[j].catch_op) {
b = blocks + block_map[op_array->try_catch_array[j].catch_op];
b->flags |= ZEND_BB_CATCH;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
if (op_array->try_catch_array[j].finally_op) {
b = blocks + block_map[op_array->try_catch_array[j].finally_op];
b->flags |= ZEND_BB_FINALLY;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
if (op_array->try_catch_array[j].finally_end) {
b = blocks + block_map[op_array->try_catch_array[j].finally_end];
b->flags |= ZEND_BB_FINALLY_END;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
} else {
if (op_array->try_catch_array[j].catch_op) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
}
if (op_array->try_catch_array[j].finally_op) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
}
if (op_array->try_catch_array[j].finally_end) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
}
}
}
} while (changed);
}
}
/* }}} */
static void initialize_block(zend_basic_block *block) {
block->flags = 0;
block->successors = block->successors_storage;
block->successors_count = 0;
block->predecessors_count = 0;
block->predecessor_offset = -1;
block->idom = -1;
block->loop_header = -1;
block->level = -1;
block->children = -1;
block->next_child = -1;
}
#define BB_START(i) do { \
if (!block_map[i]) { blocks_count++;} \
block_map[i]++; \
} while (0)
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
{
uint32_t flags = 0;
uint32_t i;
int j;
uint32_t *block_map;
zend_function *fn;
int blocks_count = 0;
zend_basic_block *blocks;
zval *zv;
zend_bool extra_entry_block = 0;
cfg->split_at_live_ranges = (build_flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES) != 0;
cfg->split_at_calls = (build_flags & ZEND_CFG_STACKLESS) != 0;
cfg->split_at_recv = (build_flags & ZEND_CFG_RECV_ENTRY) != 0 && (op_array->fn_flags & ZEND_ACC_HAS_TYPE_HINTS) == 0;
cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
/* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
BB_START(0);
for (i = 0; i < op_array->last; i++) {
zend_op *opline = op_array->opcodes + i;
switch(opline->opcode) {
case ZEND_RECV:
case ZEND_RECV_INIT:
if (build_flags & ZEND_CFG_RECV_ENTRY) {
BB_START(i + 1);
}
break;
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_INCLUDE_OR_EVAL:
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
case ZEND_GENERATOR_CREATE:
case ZEND_YIELD:
case ZEND_YIELD_FROM:
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
case ZEND_DO_FCALL:
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
flags |= ZEND_FUNC_HAS_CALLS;
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
case ZEND_DO_ICALL:
flags |= ZEND_FUNC_HAS_CALLS;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_NS_FCALL_BY_NAME:
zv = CRT_CONSTANT(opline->op2);
if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
/* The third literal is the lowercased unqualified name */
zv += 2;
}
if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
if (fn->type == ZEND_INTERNAL_FUNCTION) {
flags |= zend_cfg_classify_function(
Z_STR_P(zv), opline->extended_value);
}
}
break;
case ZEND_FAST_CALL:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_FAST_RET:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMP:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMPZNZ:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_CATCH:
if (!opline->result.num) {
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
}
BB_START(i + 1);
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
BB_START(i + 1);
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
} ZEND_HASH_FOREACH_END();
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
BB_START(i + 1);
break;
}
case ZEND_UNSET_VAR:
case ZEND_ISSET_ISEMPTY_VAR:
if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
(opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
!op_array->function_name) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
}
break;
case ZEND_FETCH_R:
case ZEND_FETCH_W:
case ZEND_FETCH_RW:
case ZEND_FETCH_FUNC_ARG:
case ZEND_FETCH_IS:
case ZEND_FETCH_UNSET:
if ((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_LOCAL) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (((opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL ||
(opline->extended_value & ZEND_FETCH_TYPE_MASK) == ZEND_FETCH_GLOBAL_LOCK) &&
!op_array->function_name) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
}
break;
case ZEND_FUNC_GET_ARGS:
flags |= ZEND_FUNC_VARARG;
break;
}
}
/* If the entry block has predecessors, we may need to split it */
if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
&& op_array->last > 0 && block_map[0] > 1) {
extra_entry_block = 1;
}
if (cfg->split_at_live_ranges) {
for (j = 0; j < op_array->last_live_range; j++) {
BB_START(op_array->live_range[j].start);
BB_START(op_array->live_range[j].end);
}
}
if (op_array->last_try_catch) {
for (j = 0; j < op_array->last_try_catch; j++) {
BB_START(op_array->try_catch_array[j].try_op);
if (op_array->try_catch_array[j].catch_op) {
BB_START(op_array->try_catch_array[j].catch_op);
}
if (op_array->try_catch_array[j].finally_op) {
BB_START(op_array->try_catch_array[j].finally_op);
}
if (op_array->try_catch_array[j].finally_end) {
BB_START(op_array->try_catch_array[j].finally_end);
}
}
}
blocks_count += extra_entry_block;
cfg->blocks_count = blocks_count;
/* Build CFG, Step 2: Build Array of Basic Blocks */
cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
blocks_count = -1;
if (extra_entry_block) {
initialize_block(&blocks[0]);
blocks[0].start = 0;
blocks[0].len = 0;
blocks_count++;
}
for (i = 0; i < op_array->last; i++) {
if (block_map[i]) {
if (blocks_count >= 0) {
blocks[blocks_count].len = i - blocks[blocks_count].start;
}
blocks_count++;
initialize_block(&blocks[blocks_count]);
blocks[blocks_count].start = i;
}
block_map[i] = blocks_count;
}
blocks[blocks_count].len = i - blocks[blocks_count].start;
blocks_count++;
/* Build CFG, Step 3: Calculate successors */
for (j = 0; j < blocks_count; j++) {
zend_basic_block *block = &blocks[j];
zend_op *opline;
if (block->len == 0) {
block->successors_count = 1;
block->successors[0] = j + 1;
continue;
}
opline = op_array->opcodes + block->start + block->len - 1;
switch (opline->opcode) {
case ZEND_FAST_RET:
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
break;
case ZEND_JMP:
block->successors_count = 1;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
break;
case ZEND_JMPZNZ:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_CATCH:
if (!opline->result.num) {
block->successors_count = 2;
block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[1] = j + 1;
} else {
block->successors_count = 1;
block->successors[0] = j + 1;
}
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
block->successors_count = 2;
block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[1] = j + 1;
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_FAST_CALL:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
uint32_t s = 0;
block->successors_count = 2 + zend_hash_num_elements(jumptable);
block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
} ZEND_HASH_FOREACH_END();
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[s++] = j + 1;
break;
}
default:
block->successors_count = 1;
block->successors[0] = j + 1;
break;
}
}
/* Build CFG, Step 4, Mark Reachable Basic Blocks */
zend_mark_reachable_blocks(op_array, cfg, 0);
cfg->dynamic = (flags & ZEND_FUNC_INDIRECT_VAR_ACCESS) != 0;
cfg->vararg = (flags & ZEND_FUNC_VARARG) != 0;
return SUCCESS;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/702/zend_worklist.h 0000664 0001750 0001750 00000007670 14724013203 015342 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andy Wingo |
+----------------------------------------------------------------------+
*/
/* $Id:$ */
#ifndef _ZEND_WORKLIST_H_
#define _ZEND_WORKLIST_H_
#include "zend_arena.h"
#include "zend_bitset.h"
typedef struct _zend_worklist_stack {
int *buf;
int len;
int capacity;
} zend_worklist_stack;
#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
(s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
(s)->len = 0; \
(s)->capacity = _len; \
} while (0)
#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
free_alloca((s)->buf, use_heap)
static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
{
ZEND_ASSERT(len >= 0);
stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
stack->len = 0;
stack->capacity = len;
return SUCCESS;
}
static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
{
ZEND_ASSERT(stack->len < stack->capacity);
stack->buf[stack->len++] = i;
}
static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
{
ZEND_ASSERT(stack->len);
return stack->buf[stack->len - 1];
}
static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
{
ZEND_ASSERT(stack->len);
return stack->buf[--stack->len];
}
typedef struct _zend_worklist {
zend_bitset visited;
zend_worklist_stack stack;
} zend_worklist;
#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
(w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
(w)->stack.len = 0; \
(w)->stack.capacity = _len; \
(w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
} while (0)
#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
free_alloca((w)->stack.buf, use_heap)
static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
{
ZEND_ASSERT(len >= 0);
worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
return zend_worklist_stack_prepare(arena, &worklist->stack, len);
}
static inline int zend_worklist_len(zend_worklist *worklist)
{
return worklist->stack.len;
}
static inline int zend_worklist_push(zend_worklist *worklist, int i)
{
ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
if (zend_bitset_in(worklist->visited, i)) {
return 0;
}
zend_bitset_incl(worklist->visited, i);
zend_worklist_stack_push(&worklist->stack, i);
return 1;
}
static inline int zend_worklist_peek(zend_worklist *worklist)
{
return zend_worklist_stack_peek(&worklist->stack);
}
static inline int zend_worklist_pop(zend_worklist *worklist)
{
/* Does not clear visited flag */
return zend_worklist_stack_pop(&worklist->stack);
}
#endif /* _ZEND_WORKLIST_H_ */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/703/zend_cfg.h 0000664 0001750 0001750 00000013062 14724013203 014214 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_CFG_H
#define ZEND_CFG_H
/* zend_basic_bloc.flags */
#define ZEND_BB_START (1<<0) /* fist block */
#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
#define ZEND_BB_TARGET (1<<2) /* jump taget */
#define ZEND_BB_EXIT (1<<3) /* without successors */
#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
#define ZEND_BB_TRY (1<<5) /* start of try block */
#define ZEND_BB_CATCH (1<<6) /* start of catch block */
#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
#define ZEND_BB_GEN_VAR (1<<9) /* start of live range */
#define ZEND_BB_KILL_VAR (1<<10) /* end of live range */
#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
#define ZEND_BB_LOOP_HEADER (1<<16)
#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
#define ZEND_BB_REACHABLE (1<<31)
#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_GEN_VAR|ZEND_BB_KILL_VAR)
typedef struct _zend_basic_block {
int *successors; /* successor block indices */
uint32_t flags;
uint32_t start; /* first opcode number */
uint32_t len; /* number of opcodes */
int successors_count; /* number of successors */
int predecessors_count; /* number of predecessors */
int predecessor_offset; /* offset of 1-st predecessor */
int idom; /* immediate dominator block */
int loop_header; /* closest loop header, or -1 */
int level; /* steps away from the entry in the dom. tree */
int children; /* list of dominated blocks */
int next_child; /* next dominated block */
int successors_storage[2]; /* up to 2 successor blocks */
} zend_basic_block;
/*
+------------+---+---+---+---+---+
| |OP1|OP2|EXT| 0 | 1 |
+------------+---+---+---+---+---+
|JMP |ADR| | |OP1| - |
|JMPZ | |ADR| |OP2|FOL|
|JMPNZ | |ADR| |OP2|FOL|
|JMPZNZ | |ADR|ADR|OP2|EXT|
|JMPZ_EX | |ADR| |OP2|FOL|
|JMPNZ_EX | |ADR| |OP2|FOL|
|JMP_SET | |ADR| |OP2|FOL|
|COALESCE | |ADR| |OP2|FOL|
|ASSERT_CHK | |ADR| |OP2|FOL|
|NEW | |ADR| |OP2|FOL|
|DCL_ANON* |ADR| | |OP1|FOL|
|FE_RESET_* | |ADR| |OP2|FOL|
|FE_FETCH_* | | |ADR|EXT|FOL|
|CATCH | | |ADR|EXT|FOL|
|FAST_CALL |ADR| | |OP1|FOL|
|FAST_RET | | | | - | - |
|RETURN* | | | | - | - |
|EXIT | | | | - | - |
|THROW | | | | - | - |
|* | | | |FOL| - |
+------------+---+---+---+---+---+
*/
typedef struct _zend_cfg {
int blocks_count; /* number of basic blocks */
int edges_count; /* number of edges */
zend_basic_block *blocks; /* array of basic blocks */
int *predecessors;
uint32_t *map;
uint32_t flags;
} zend_cfg;
/* Build Flags */
#define ZEND_RT_CONSTANTS (1<<31)
#define ZEND_CFG_STACKLESS (1<<30)
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
#define ZEND_SSA_RC_INFERENCE (1<<27)
#define ZEND_CFG_SPLIT_AT_LIVE_RANGES (1<<26)
#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
#define ZEND_CFG_RECV_ENTRY (1<<24)
#define ZEND_CALL_TREE (1<<23)
#define ZEND_SSA_USE_CV_RESULTS (1<<22)
#define CRT_CONSTANT_EX(op_array, opline, node, rt_constants) \
((rt_constants) ? \
RT_CONSTANT(opline, (node)) \
: \
CT_CONSTANT_EX(op_array, (node).constant) \
)
#define CRT_CONSTANT(node) \
CRT_CONSTANT_EX(op_array, opline, node, (build_flags & ZEND_RT_CONSTANTS))
#define RETURN_VALUE_USED(opline) \
((opline)->result_type != IS_UNUSED)
BEGIN_EXTERN_C()
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
END_EXTERN_C()
#endif /* ZEND_CFG_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/703/zend_cfg.c 0000664 0001750 0001750 00000047532 14724013203 014220 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov |
+----------------------------------------------------------------------+
This source file has been adapted for pcov so that the CFG from O+ is standalone
*/
#include "php.h"
#include "zend_compile.h"
#include "zend_cfg.h"
#include "zend_worklist.h"
/* func flags */
#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
#define ZEND_FUNC_HAS_CALLS (1<<1)
#define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
#define ZEND_FUNC_NO_LOOPS (1<<3)
#define ZEND_FUNC_IRREDUCIBLE (1<<4)
#define ZEND_FUNC_RECURSIVE (1<<7)
#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
#define ZEND_FUNC_HAS_EXTENDED_INFO (1<<10)
/* The following flags are valid only for return values of internal functions
* returned by zend_get_func_info()
*/
#define FUNC_MAY_WARN (1<<30)
typedef struct _zend_func_info zend_func_info;
typedef struct _zend_call_info zend_call_info;
#define ZEND_FUNC_INFO(op_array) \
((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
#define ZEND_SET_FUNC_INFO(op_array, info) do { \
zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
*pinfo = info; \
} while (0)
static uint32_t zend_cfg_classify_function(zend_string *name, uint32_t num_args) {
if (zend_string_equals_literal(name, "extract")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "compact")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "get_defined_vars")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "assert")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "func_num_args")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_arg")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_args")) {
return ZEND_FUNC_VARARG;
} else {
return 0;
}
}
static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
while (1) {
int i;
b->flags |= ZEND_BB_REACHABLE;
if (b->successors_count == 0) {
b->flags |= ZEND_BB_EXIT;
return;
}
for (i = 0; i < b->successors_count; i++) {
zend_basic_block *succ = blocks + b->successors[i];
if (b->len != 0) {
zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
if (b->successors_count == 1) {
if (opcode == ZEND_JMP) {
succ->flags |= ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_FOLLOW;
if ((cfg->flags & ZEND_CFG_STACKLESS)) {
if (opcode == ZEND_INCLUDE_OR_EVAL ||
opcode == ZEND_GENERATOR_CREATE ||
opcode == ZEND_YIELD ||
opcode == ZEND_YIELD_FROM ||
opcode == ZEND_DO_FCALL ||
opcode == ZEND_DO_UCALL ||
opcode == ZEND_DO_FCALL_BY_NAME) {
succ->flags |= ZEND_BB_ENTRY;
}
}
if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
if (opcode == ZEND_RECV ||
opcode == ZEND_RECV_INIT) {
succ->flags |= ZEND_BB_RECV_ENTRY;
}
}
}
} else if (b->successors_count == 2) {
if (i == 0 || opcode == ZEND_JMPZNZ) {
succ->flags |= ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_FOLLOW;
}
} else {
ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
if (i == b->successors_count - 1) {
succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_TARGET;
}
}
} else {
succ->flags |= ZEND_BB_FOLLOW;
}
if (i == b->successors_count - 1) {
/* Tail call optimization */
if (succ->flags & ZEND_BB_REACHABLE) {
return;
}
b = succ;
break;
} else {
/* Recusively check reachability */
if (!(succ->flags & ZEND_BB_REACHABLE)) {
zend_mark_reachable(opcodes, cfg, succ);
}
}
}
}
}
/* }}} */
static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
blocks[start].flags = ZEND_BB_START;
zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
if (op_array->last_live_range || op_array->last_try_catch) {
zend_basic_block *b;
int j, changed;
uint32_t *block_map = cfg->map;
do {
changed = 0;
/* Add live range paths */
for (j = 0; j < op_array->last_live_range; j++) {
zend_live_range *live_range = &op_array->live_range[j];
if (live_range->var == (uint32_t)-1) {
/* this live range already removed */
continue;
}
b = blocks + block_map[live_range->start];
if (b->flags & ZEND_BB_REACHABLE) {
while (b->len > 0 && op_array->opcodes[b->start].opcode == ZEND_NOP) {
/* check if NOP breaks incorrect smart branch */
if (b->len == 2
&& (op_array->opcodes[b->start + 1].opcode == ZEND_JMPZ
|| op_array->opcodes[b->start + 1].opcode == ZEND_JMPNZ)
&& (op_array->opcodes[b->start + 1].op1_type & (IS_CV|IS_CONST))
&& b->start > 0
&& zend_is_smart_branch(op_array->opcodes + b->start - 1)) {
break;
}
b->start++;
b->len--;
}
if (b->len == 0 && (uint32_t)b->successors[0] == block_map[live_range->end]) {
/* mark as removed (empty live range) */
live_range->var = (uint32_t)-1;
continue;
}
b->flags |= ZEND_BB_GEN_VAR;
b = blocks + block_map[live_range->end];
b->flags |= ZEND_BB_KILL_VAR;
if (!(b->flags & (ZEND_BB_REACHABLE|ZEND_BB_UNREACHABLE_FREE))) {
if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
} else {
b->flags |= ZEND_BB_UNREACHABLE_FREE;
}
}
} else {
ZEND_ASSERT(!(blocks[block_map[live_range->end]].flags & ZEND_BB_REACHABLE));
}
}
/* Add exception paths */
for (j = 0; j < op_array->last_try_catch; j++) {
/* check for jumps into the middle of try block */
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (!(b->flags & ZEND_BB_REACHABLE)) {
zend_basic_block *end;
if (op_array->try_catch_array[j].catch_op) {
end = blocks + block_map[op_array->try_catch_array[j].catch_op];
while (b != end) {
if (b->flags & ZEND_BB_REACHABLE) {
op_array->try_catch_array[j].try_op = b->start;
break;
}
b++;
}
}
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (!(b->flags & ZEND_BB_REACHABLE)) {
if (op_array->try_catch_array[j].finally_op) {
end = blocks + block_map[op_array->try_catch_array[j].finally_op];
while (b != end) {
if (b->flags & ZEND_BB_REACHABLE) {
op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
break;
}
b++;
}
}
}
}
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (b->flags & ZEND_BB_REACHABLE) {
b->flags |= ZEND_BB_TRY;
if (op_array->try_catch_array[j].catch_op) {
b = blocks + block_map[op_array->try_catch_array[j].catch_op];
b->flags |= ZEND_BB_CATCH;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
if (op_array->try_catch_array[j].finally_op) {
b = blocks + block_map[op_array->try_catch_array[j].finally_op];
b->flags |= ZEND_BB_FINALLY;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
if (op_array->try_catch_array[j].finally_end) {
b = blocks + block_map[op_array->try_catch_array[j].finally_end];
b->flags |= ZEND_BB_FINALLY_END;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
} else {
if (op_array->try_catch_array[j].catch_op) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
}
if (op_array->try_catch_array[j].finally_op) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
}
if (op_array->try_catch_array[j].finally_end) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
}
}
}
} while (changed);
}
}
/* }}} */
static void initialize_block(zend_basic_block *block) {
block->flags = 0;
block->successors = block->successors_storage;
block->successors_count = 0;
block->predecessors_count = 0;
block->predecessor_offset = -1;
block->idom = -1;
block->loop_header = -1;
block->level = -1;
block->children = -1;
block->next_child = -1;
}
#define BB_START(i) do { \
if (!block_map[i]) { blocks_count++;} \
block_map[i]++; \
} while (0)
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
{
uint32_t flags = 0;
uint32_t i;
int j;
uint32_t *block_map;
zend_function *fn;
int blocks_count = 0;
zend_basic_block *blocks;
zval *zv;
zend_bool extra_entry_block = 0;
cfg->flags = build_flags & (ZEND_CFG_SPLIT_AT_LIVE_RANGES|ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
/* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
BB_START(0);
for (i = 0; i < op_array->last; i++) {
zend_op *opline = op_array->opcodes + i;
switch (opline->opcode) {
case ZEND_RECV:
case ZEND_RECV_INIT:
if (build_flags & ZEND_CFG_RECV_ENTRY) {
BB_START(i + 1);
}
break;
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_INCLUDE_OR_EVAL:
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
case ZEND_GENERATOR_CREATE:
case ZEND_YIELD:
case ZEND_YIELD_FROM:
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
case ZEND_DO_FCALL:
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
flags |= ZEND_FUNC_HAS_CALLS;
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
case ZEND_DO_ICALL:
flags |= ZEND_FUNC_HAS_CALLS;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_NS_FCALL_BY_NAME:
zv = CRT_CONSTANT(opline->op2);
if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
/* The third literal is the lowercased unqualified name */
zv += 2;
}
if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
if (fn->type == ZEND_INTERNAL_FUNCTION) {
flags |= zend_cfg_classify_function(
Z_STR_P(zv), opline->extended_value);
}
}
break;
case ZEND_FAST_CALL:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_FAST_RET:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMP:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMPZNZ:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_CATCH:
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
}
BB_START(i + 1);
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
BB_START(i + 1);
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
} ZEND_HASH_FOREACH_END();
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
BB_START(i + 1);
break;
}
case ZEND_UNSET_VAR:
case ZEND_ISSET_ISEMPTY_VAR:
if (opline->extended_value & ZEND_FETCH_LOCAL) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
!op_array->function_name) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
}
break;
case ZEND_FETCH_R:
case ZEND_FETCH_W:
case ZEND_FETCH_RW:
case ZEND_FETCH_FUNC_ARG:
case ZEND_FETCH_IS:
case ZEND_FETCH_UNSET:
if (opline->extended_value & ZEND_FETCH_LOCAL) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
!op_array->function_name) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
}
break;
case ZEND_FUNC_GET_ARGS:
flags |= ZEND_FUNC_VARARG;
break;
case ZEND_EXT_NOP:
case ZEND_EXT_STMT:
case ZEND_EXT_FCALL_BEGIN:
case ZEND_EXT_FCALL_END:
flags |= ZEND_FUNC_HAS_EXTENDED_INFO;
break;
}
}
/* If the entry block has predecessors, we may need to split it */
if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
&& op_array->last > 0 && block_map[0] > 1) {
extra_entry_block = 1;
}
if ((cfg->flags & ZEND_CFG_SPLIT_AT_LIVE_RANGES)) {
for (j = 0; j < op_array->last_live_range; j++) {
BB_START(op_array->live_range[j].start);
BB_START(op_array->live_range[j].end);
}
}
if (op_array->last_try_catch) {
for (j = 0; j < op_array->last_try_catch; j++) {
BB_START(op_array->try_catch_array[j].try_op);
if (op_array->try_catch_array[j].catch_op) {
BB_START(op_array->try_catch_array[j].catch_op);
}
if (op_array->try_catch_array[j].finally_op) {
BB_START(op_array->try_catch_array[j].finally_op);
}
if (op_array->try_catch_array[j].finally_end) {
BB_START(op_array->try_catch_array[j].finally_end);
}
}
}
blocks_count += extra_entry_block;
cfg->blocks_count = blocks_count;
/* Build CFG, Step 2: Build Array of Basic Blocks */
cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
blocks_count = -1;
if (extra_entry_block) {
initialize_block(&blocks[0]);
blocks[0].start = 0;
blocks[0].len = 0;
blocks_count++;
}
for (i = 0; i < op_array->last; i++) {
if (block_map[i]) {
if (blocks_count >= 0) {
blocks[blocks_count].len = i - blocks[blocks_count].start;
}
blocks_count++;
initialize_block(&blocks[blocks_count]);
blocks[blocks_count].start = i;
}
block_map[i] = blocks_count;
}
blocks[blocks_count].len = i - blocks[blocks_count].start;
blocks_count++;
/* Build CFG, Step 3: Calculate successors */
for (j = 0; j < blocks_count; j++) {
zend_basic_block *block = &blocks[j];
zend_op *opline;
if (block->len == 0) {
block->successors_count = 1;
block->successors[0] = j + 1;
continue;
}
opline = op_array->opcodes + block->start + block->len - 1;
switch (opline->opcode) {
case ZEND_FAST_RET:
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
break;
case ZEND_JMP:
block->successors_count = 1;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
break;
case ZEND_JMPZNZ:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_CATCH:
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = j + 1;
} else {
block->successors_count = 1;
block->successors[0] = j + 1;
}
break;
case ZEND_DECLARE_ANON_CLASS:
case ZEND_DECLARE_ANON_INHERITED_CLASS:
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
block->successors_count = 2;
block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[1] = j + 1;
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_FAST_CALL:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
uint32_t s = 0;
block->successors_count = 2 + zend_hash_num_elements(jumptable);
block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
} ZEND_HASH_FOREACH_END();
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[s++] = j + 1;
break;
}
default:
block->successors_count = 1;
block->successors[0] = j + 1;
break;
}
}
/* Build CFG, Step 4, Mark Reachable Basic Blocks */
zend_mark_reachable_blocks(op_array, cfg, 0);
cfg->flags |= flags;
return SUCCESS;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/703/zend_worklist.h 0000664 0001750 0001750 00000007653 14724013203 015344 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2018 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andy Wingo |
+----------------------------------------------------------------------+
*/
#ifndef _ZEND_WORKLIST_H_
#define _ZEND_WORKLIST_H_
#include "zend_arena.h"
#include "zend_bitset.h"
typedef struct _zend_worklist_stack {
int *buf;
int len;
int capacity;
} zend_worklist_stack;
#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
(s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
(s)->len = 0; \
(s)->capacity = _len; \
} while (0)
#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
free_alloca((s)->buf, use_heap)
static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
{
ZEND_ASSERT(len >= 0);
stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
stack->len = 0;
stack->capacity = len;
return SUCCESS;
}
static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
{
ZEND_ASSERT(stack->len < stack->capacity);
stack->buf[stack->len++] = i;
}
static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
{
ZEND_ASSERT(stack->len);
return stack->buf[stack->len - 1];
}
static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
{
ZEND_ASSERT(stack->len);
return stack->buf[--stack->len];
}
typedef struct _zend_worklist {
zend_bitset visited;
zend_worklist_stack stack;
} zend_worklist;
#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
(w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
(w)->stack.len = 0; \
(w)->stack.capacity = _len; \
(w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
} while (0)
#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
free_alloca((w)->stack.buf, use_heap)
static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
{
ZEND_ASSERT(len >= 0);
worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
return zend_worklist_stack_prepare(arena, &worklist->stack, len);
}
static inline int zend_worklist_len(zend_worklist *worklist)
{
return worklist->stack.len;
}
static inline int zend_worklist_push(zend_worklist *worklist, int i)
{
ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
if (zend_bitset_in(worklist->visited, i)) {
return 0;
}
zend_bitset_incl(worklist->visited, i);
zend_worklist_stack_push(&worklist->stack, i);
return 1;
}
static inline int zend_worklist_peek(zend_worklist *worklist)
{
return zend_worklist_stack_peek(&worklist->stack);
}
static inline int zend_worklist_pop(zend_worklist *worklist)
{
/* Does not clear visited flag */
return zend_worklist_stack_pop(&worklist->stack);
}
#endif /* _ZEND_WORKLIST_H_ */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/
pcov-1.0.12/cfg/704/zend_cfg.h 0000664 0001750 0001750 00000013114 14724013203 014213 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov |
+----------------------------------------------------------------------+
*/
#ifndef ZEND_CFG_H
#define ZEND_CFG_H
/* zend_basic_bloc.flags */
#define ZEND_BB_START (1<<0) /* fist block */
#define ZEND_BB_FOLLOW (1<<1) /* follows the next block */
#define ZEND_BB_TARGET (1<<2) /* jump taget */
#define ZEND_BB_EXIT (1<<3) /* without successors */
#define ZEND_BB_ENTRY (1<<4) /* stackless entry */
#define ZEND_BB_TRY (1<<5) /* start of try block */
#define ZEND_BB_CATCH (1<<6) /* start of catch block */
#define ZEND_BB_FINALLY (1<<7) /* start of finally block */
#define ZEND_BB_FINALLY_END (1<<8) /* end of finally block */
#define ZEND_BB_UNREACHABLE_FREE (1<<11) /* unreachable loop free */
#define ZEND_BB_RECV_ENTRY (1<<12) /* RECV entry */
#define ZEND_BB_LOOP_HEADER (1<<16)
#define ZEND_BB_IRREDUCIBLE_LOOP (1<<17)
#define ZEND_BB_REACHABLE (1U<<31)
#define ZEND_BB_PROTECTED (ZEND_BB_ENTRY|ZEND_BB_RECV_ENTRY|ZEND_BB_TRY|ZEND_BB_CATCH|ZEND_BB_FINALLY|ZEND_BB_FINALLY_END|ZEND_BB_UNREACHABLE_FREE)
typedef struct _zend_basic_block {
int *successors; /* successor block indices */
uint32_t flags;
uint32_t start; /* first opcode number */
uint32_t len; /* number of opcodes */
int successors_count; /* number of successors */
int predecessors_count; /* number of predecessors */
int predecessor_offset; /* offset of 1-st predecessor */
int idom; /* immediate dominator block */
int loop_header; /* closest loop header, or -1 */
int level; /* steps away from the entry in the dom. tree */
int children; /* list of dominated blocks */
int next_child; /* next dominated block */
int successors_storage[2]; /* up to 2 successor blocks */
} zend_basic_block;
/*
+------------+---+---+---+---+---+
| |OP1|OP2|EXT| 0 | 1 |
+------------+---+---+---+---+---+
|JMP |ADR| | |OP1| - |
|JMPZ | |ADR| |OP2|FOL|
|JMPNZ | |ADR| |OP2|FOL|
|JMPZNZ | |ADR|ADR|OP2|EXT|
|JMPZ_EX | |ADR| |OP2|FOL|
|JMPNZ_EX | |ADR| |OP2|FOL|
|JMP_SET | |ADR| |OP2|FOL|
|COALESCE | |ADR| |OP2|FOL|
|ASSERT_CHK | |ADR| |OP2|FOL|
|NEW | |ADR| |OP2|FOL|
|DCL_ANON* |ADR| | |OP1|FOL|
|FE_RESET_* | |ADR| |OP2|FOL|
|FE_FETCH_* | | |ADR|EXT|FOL|
|CATCH | | |ADR|EXT|FOL|
|FAST_CALL |ADR| | |OP1|FOL|
|FAST_RET | | | | - | - |
|RETURN* | | | | - | - |
|EXIT | | | | - | - |
|THROW | | | | - | - |
|* | | | |FOL| - |
+------------+---+---+---+---+---+
*/
typedef struct _zend_cfg {
int blocks_count; /* number of basic blocks */
int edges_count; /* number of edges */
zend_basic_block *blocks; /* array of basic blocks */
int *predecessors;
uint32_t *map;
uint32_t flags;
} zend_cfg;
/* Build Flags */
#define ZEND_RT_CONSTANTS (1U<<31)
#define ZEND_CFG_STACKLESS (1<<30)
#define ZEND_SSA_DEBUG_LIVENESS (1<<29)
#define ZEND_SSA_DEBUG_PHI_PLACEMENT (1<<28)
#define ZEND_SSA_RC_INFERENCE (1<<27)
#define ZEND_CFG_NO_ENTRY_PREDECESSORS (1<<25)
#define ZEND_CFG_RECV_ENTRY (1<<24)
#define ZEND_CALL_TREE (1<<23)
#define ZEND_SSA_USE_CV_RESULTS (1<<22)
#define CRT_CONSTANT_EX(op_array, opline, node, rt_constants) \
((rt_constants) ? \
RT_CONSTANT(opline, (node)) \
: \
CT_CONSTANT_EX(op_array, (node).constant) \
)
#define CRT_CONSTANT(node) \
CRT_CONSTANT_EX(op_array, opline, node, (build_flags & ZEND_RT_CONSTANTS))
#define RETURN_VALUE_USED(opline) \
((opline)->result_type != IS_UNUSED)
BEGIN_EXTERN_C()
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg);
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg);
int zend_cfg_build_predecessors(zend_arena **arena, zend_cfg *cfg);
int zend_cfg_compute_dominators_tree(const zend_op_array *op_array, zend_cfg *cfg);
int zend_cfg_identify_loops(const zend_op_array *op_array, zend_cfg *cfg);
END_EXTERN_C()
#endif /* ZEND_CFG_H */
pcov-1.0.12/cfg/704/zend_cfg.c 0000664 0001750 0001750 00000044714 14724013203 014220 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine, CFG - Control Flow Graph |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Dmitry Stogov |
+----------------------------------------------------------------------+
This source file has been adapted for pcov so that the CFG from O+ is standalone
*/
#include "php.h"
#include "zend_compile.h"
#include "zend_cfg.h"
#include "zend_worklist.h"
/* func flags */
#define ZEND_FUNC_INDIRECT_VAR_ACCESS (1<<0) /* accesses variables by name */
#define ZEND_FUNC_HAS_CALLS (1<<1)
#define ZEND_FUNC_VARARG (1<<2) /* uses func_get_args() */
#define ZEND_FUNC_NO_LOOPS (1<<3)
#define ZEND_FUNC_IRREDUCIBLE (1<<4)
#define ZEND_FUNC_FREE_LOOP_VAR (1<<5)
#define ZEND_FUNC_RECURSIVE (1<<7)
#define ZEND_FUNC_RECURSIVE_DIRECTLY (1<<8)
#define ZEND_FUNC_RECURSIVE_INDIRECTLY (1<<9)
#define ZEND_FUNC_HAS_EXTENDED_FCALL (1<<10)
#define ZEND_FUNC_HAS_EXTENDED_STMT (1<<11)
/* The following flags are valid only for return values of internal functions
* returned by zend_get_func_info()
*/
#define FUNC_MAY_WARN (1<<30)
typedef struct _zend_func_info zend_func_info;
typedef struct _zend_call_info zend_call_info;
#define ZEND_FUNC_INFO(op_array) \
((zend_func_info*)((op_array)->reserved[zend_func_info_rid]))
#define ZEND_SET_FUNC_INFO(op_array, info) do { \
zend_func_info** pinfo = (zend_func_info**)&(op_array)->reserved[zend_func_info_rid]; \
*pinfo = info; \
} while (0)
static uint32_t zend_cfg_classify_function(zend_string *name, uint32_t num_args) {
if (zend_string_equals_literal(name, "extract")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "compact")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "parse_str") && num_args <= 1) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "mb_parse_str") && num_args <= 1) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "get_defined_vars")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "assert")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "db2_execute")) {
return ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if (zend_string_equals_literal(name, "func_num_args")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_arg")) {
return ZEND_FUNC_VARARG;
} else if (zend_string_equals_literal(name, "func_get_args")) {
return ZEND_FUNC_VARARG;
} else {
return 0;
}
}
static void zend_mark_reachable(zend_op *opcodes, zend_cfg *cfg, zend_basic_block *b) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
while (1) {
int i;
b->flags |= ZEND_BB_REACHABLE;
if (b->successors_count == 0) {
b->flags |= ZEND_BB_EXIT;
return;
}
for (i = 0; i < b->successors_count; i++) {
zend_basic_block *succ = blocks + b->successors[i];
if (b->len != 0) {
zend_uchar opcode = opcodes[b->start + b->len - 1].opcode;
if (b->successors_count == 1) {
if (opcode == ZEND_JMP) {
succ->flags |= ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_FOLLOW;
if ((cfg->flags & ZEND_CFG_STACKLESS)) {
if (opcode == ZEND_INCLUDE_OR_EVAL ||
opcode == ZEND_GENERATOR_CREATE ||
opcode == ZEND_YIELD ||
opcode == ZEND_YIELD_FROM ||
opcode == ZEND_DO_FCALL ||
opcode == ZEND_DO_UCALL ||
opcode == ZEND_DO_FCALL_BY_NAME) {
succ->flags |= ZEND_BB_ENTRY;
}
}
if ((cfg->flags & ZEND_CFG_RECV_ENTRY)) {
if (opcode == ZEND_RECV ||
opcode == ZEND_RECV_INIT) {
succ->flags |= ZEND_BB_RECV_ENTRY;
}
}
}
} else if (b->successors_count == 2) {
if (i == 0 || opcode == ZEND_JMPZNZ) {
succ->flags |= ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_FOLLOW;
}
} else {
ZEND_ASSERT(opcode == ZEND_SWITCH_LONG || opcode == ZEND_SWITCH_STRING);
if (i == b->successors_count - 1) {
succ->flags |= ZEND_BB_FOLLOW | ZEND_BB_TARGET;
} else {
succ->flags |= ZEND_BB_TARGET;
}
}
} else {
succ->flags |= ZEND_BB_FOLLOW;
}
if (i == b->successors_count - 1) {
/* Tail call optimization */
if (succ->flags & ZEND_BB_REACHABLE) {
return;
}
b = succ;
break;
} else {
/* Recusively check reachability */
if (!(succ->flags & ZEND_BB_REACHABLE)) {
zend_mark_reachable(opcodes, cfg, succ);
}
}
}
}
}
/* }}} */
static void zend_mark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg, int start) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
blocks[start].flags = ZEND_BB_START;
zend_mark_reachable(op_array->opcodes, cfg, blocks + start);
if (op_array->last_try_catch) {
zend_basic_block *b;
int j, changed;
uint32_t *block_map = cfg->map;
do {
changed = 0;
/* Add exception paths */
for (j = 0; j < op_array->last_try_catch; j++) {
/* check for jumps into the middle of try block */
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (!(b->flags & ZEND_BB_REACHABLE)) {
zend_basic_block *end;
if (op_array->try_catch_array[j].catch_op) {
end = blocks + block_map[op_array->try_catch_array[j].catch_op];
while (b != end) {
if (b->flags & ZEND_BB_REACHABLE) {
op_array->try_catch_array[j].try_op = b->start;
break;
}
b++;
}
}
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (!(b->flags & ZEND_BB_REACHABLE)) {
if (op_array->try_catch_array[j].finally_op) {
end = blocks + block_map[op_array->try_catch_array[j].finally_op];
while (b != end) {
if (b->flags & ZEND_BB_REACHABLE) {
op_array->try_catch_array[j].try_op = op_array->try_catch_array[j].catch_op;
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, blocks + block_map[op_array->try_catch_array[j].try_op]);
break;
}
b++;
}
}
}
}
b = blocks + block_map[op_array->try_catch_array[j].try_op];
if (b->flags & ZEND_BB_REACHABLE) {
b->flags |= ZEND_BB_TRY;
if (op_array->try_catch_array[j].catch_op) {
b = blocks + block_map[op_array->try_catch_array[j].catch_op];
b->flags |= ZEND_BB_CATCH;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
if (op_array->try_catch_array[j].finally_op) {
b = blocks + block_map[op_array->try_catch_array[j].finally_op];
b->flags |= ZEND_BB_FINALLY;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
if (op_array->try_catch_array[j].finally_end) {
b = blocks + block_map[op_array->try_catch_array[j].finally_end];
b->flags |= ZEND_BB_FINALLY_END;
if (!(b->flags & ZEND_BB_REACHABLE)) {
changed = 1;
zend_mark_reachable(op_array->opcodes, cfg, b);
}
}
} else {
if (op_array->try_catch_array[j].catch_op) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].catch_op]].flags & ZEND_BB_REACHABLE));
}
if (op_array->try_catch_array[j].finally_op) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_op]].flags & ZEND_BB_REACHABLE));
}
if (op_array->try_catch_array[j].finally_end) {
ZEND_ASSERT(!(blocks[block_map[op_array->try_catch_array[j].finally_end]].flags & ZEND_BB_REACHABLE));
}
}
}
} while (changed);
}
}
/* }}} */
void zend_cfg_remark_reachable_blocks(const zend_op_array *op_array, zend_cfg *cfg) /* {{{ */
{
zend_basic_block *blocks = cfg->blocks;
int i;
int start = 0;
for (i = 0; i < cfg->blocks_count; i++) {
if (blocks[i].flags & ZEND_BB_REACHABLE) {
start = i;
i++;
break;
}
}
/* clear all flags */
for (i = 0; i < cfg->blocks_count; i++) {
blocks[i].flags = 0;
}
zend_mark_reachable_blocks(op_array, cfg, start);
}
/* }}} */
static void initialize_block(zend_basic_block *block) {
block->flags = 0;
block->successors = block->successors_storage;
block->successors_count = 0;
block->predecessors_count = 0;
block->predecessor_offset = -1;
block->idom = -1;
block->loop_header = -1;
block->level = -1;
block->children = -1;
block->next_child = -1;
}
#define BB_START(i) do { \
if (!block_map[i]) { blocks_count++;} \
block_map[i]++; \
} while (0)
int zend_build_cfg(zend_arena **arena, const zend_op_array *op_array, uint32_t build_flags, zend_cfg *cfg) /* {{{ */
{
uint32_t flags = 0;
uint32_t i;
int j;
uint32_t *block_map;
zend_function *fn;
int blocks_count = 0;
zend_basic_block *blocks;
zval *zv;
zend_bool extra_entry_block = 0;
cfg->flags = build_flags & (ZEND_CFG_STACKLESS|ZEND_CFG_RECV_ENTRY);
cfg->map = block_map = zend_arena_calloc(arena, op_array->last, sizeof(uint32_t));
/* Build CFG, Step 1: Find basic blocks starts, calculate number of blocks */
BB_START(0);
for (i = 0; i < op_array->last; i++) {
zend_op *opline = op_array->opcodes + i;
switch (opline->opcode) {
case ZEND_RECV:
case ZEND_RECV_INIT:
if (build_flags & ZEND_CFG_RECV_ENTRY) {
BB_START(i + 1);
}
break;
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_INCLUDE_OR_EVAL:
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
case ZEND_GENERATOR_CREATE:
case ZEND_YIELD:
case ZEND_YIELD_FROM:
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
case ZEND_DO_FCALL:
case ZEND_DO_UCALL:
case ZEND_DO_FCALL_BY_NAME:
flags |= ZEND_FUNC_HAS_CALLS;
if (build_flags & ZEND_CFG_STACKLESS) {
BB_START(i + 1);
}
break;
case ZEND_DO_ICALL:
flags |= ZEND_FUNC_HAS_CALLS;
break;
case ZEND_INIT_FCALL:
case ZEND_INIT_NS_FCALL_BY_NAME:
zv = CRT_CONSTANT(opline->op2);
if (opline->opcode == ZEND_INIT_NS_FCALL_BY_NAME) {
/* The third literal is the lowercased unqualified name */
zv += 2;
}
if ((fn = zend_hash_find_ptr(EG(function_table), Z_STR_P(zv))) != NULL) {
if (fn->type == ZEND_INTERNAL_FUNCTION) {
flags |= zend_cfg_classify_function(
Z_STR_P(zv), opline->extended_value);
}
}
break;
case ZEND_FAST_CALL:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_FAST_RET:
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMP:
BB_START(OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes);
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMPZNZ:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
if (i + 1 < op_array->last) {
BB_START(i + 1);
}
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_CATCH:
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
}
BB_START(i + 1);
break;
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
BB_START(i + 1);
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
BB_START(OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes);
BB_START(i + 1);
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv)));
} ZEND_HASH_FOREACH_END();
BB_START(ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value));
BB_START(i + 1);
break;
}
case ZEND_FETCH_R:
case ZEND_FETCH_W:
case ZEND_FETCH_RW:
case ZEND_FETCH_FUNC_ARG:
case ZEND_FETCH_IS:
case ZEND_FETCH_UNSET:
case ZEND_UNSET_VAR:
case ZEND_ISSET_ISEMPTY_VAR:
if (opline->extended_value & ZEND_FETCH_LOCAL) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
} else if ((opline->extended_value & (ZEND_FETCH_GLOBAL | ZEND_FETCH_GLOBAL_LOCK)) &&
!op_array->function_name) {
flags |= ZEND_FUNC_INDIRECT_VAR_ACCESS;
}
break;
case ZEND_FUNC_GET_ARGS:
flags |= ZEND_FUNC_VARARG;
break;
case ZEND_EXT_NOP:
case ZEND_EXT_STMT:
flags |= ZEND_FUNC_HAS_EXTENDED_STMT;
break;
case ZEND_EXT_FCALL_BEGIN:
case ZEND_EXT_FCALL_END:
flags |= ZEND_FUNC_HAS_EXTENDED_FCALL;
break;
case ZEND_FREE:
if (opline->extended_value == ZEND_FREE_SWITCH) {
flags |= ZEND_FUNC_FREE_LOOP_VAR;
}
break;
case ZEND_FE_FREE:
flags |= ZEND_FUNC_FREE_LOOP_VAR;
break;
}
}
/* If the entry block has predecessors, we may need to split it */
if ((build_flags & ZEND_CFG_NO_ENTRY_PREDECESSORS)
&& op_array->last > 0 && block_map[0] > 1) {
extra_entry_block = 1;
}
if (op_array->last_try_catch) {
for (j = 0; j < op_array->last_try_catch; j++) {
BB_START(op_array->try_catch_array[j].try_op);
if (op_array->try_catch_array[j].catch_op) {
BB_START(op_array->try_catch_array[j].catch_op);
}
if (op_array->try_catch_array[j].finally_op) {
BB_START(op_array->try_catch_array[j].finally_op);
}
if (op_array->try_catch_array[j].finally_end) {
BB_START(op_array->try_catch_array[j].finally_end);
}
}
}
blocks_count += extra_entry_block;
cfg->blocks_count = blocks_count;
/* Build CFG, Step 2: Build Array of Basic Blocks */
cfg->blocks = blocks = zend_arena_calloc(arena, sizeof(zend_basic_block), blocks_count);
blocks_count = -1;
if (extra_entry_block) {
initialize_block(&blocks[0]);
blocks[0].start = 0;
blocks[0].len = 0;
blocks_count++;
}
for (i = 0; i < op_array->last; i++) {
if (block_map[i]) {
if (blocks_count >= 0) {
blocks[blocks_count].len = i - blocks[blocks_count].start;
}
blocks_count++;
initialize_block(&blocks[blocks_count]);
blocks[blocks_count].start = i;
}
block_map[i] = blocks_count;
}
blocks[blocks_count].len = i - blocks[blocks_count].start;
blocks_count++;
/* Build CFG, Step 3: Calculate successors */
for (j = 0; j < blocks_count; j++) {
zend_basic_block *block = &blocks[j];
zend_op *opline;
if (block->len == 0) {
block->successors_count = 1;
block->successors[0] = j + 1;
continue;
}
opline = op_array->opcodes + block->start + block->len - 1;
switch (opline->opcode) {
case ZEND_FAST_RET:
case ZEND_RETURN:
case ZEND_RETURN_BY_REF:
case ZEND_GENERATOR_RETURN:
case ZEND_EXIT:
case ZEND_THROW:
break;
case ZEND_JMP:
block->successors_count = 1;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
break;
case ZEND_JMPZNZ:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
break;
case ZEND_JMPZ:
case ZEND_JMPNZ:
case ZEND_JMPZ_EX:
case ZEND_JMPNZ_EX:
case ZEND_JMP_SET:
case ZEND_COALESCE:
case ZEND_ASSERT_CHECK:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_CATCH:
if (!(opline->extended_value & ZEND_LAST_CATCH)) {
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = j + 1;
} else {
block->successors_count = 1;
block->successors[0] = j + 1;
}
break;
case ZEND_FE_FETCH_R:
case ZEND_FE_FETCH_RW:
block->successors_count = 2;
block->successors[0] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[1] = j + 1;
break;
case ZEND_FE_RESET_R:
case ZEND_FE_RESET_RW:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op2) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_FAST_CALL:
block->successors_count = 2;
block->successors[0] = block_map[OP_JMP_ADDR(opline, opline->op1) - op_array->opcodes];
block->successors[1] = j + 1;
break;
case ZEND_SWITCH_LONG:
case ZEND_SWITCH_STRING:
{
HashTable *jumptable = Z_ARRVAL_P(CRT_CONSTANT(opline->op2));
zval *zv;
uint32_t s = 0;
block->successors_count = 2 + zend_hash_num_elements(jumptable);
block->successors = zend_arena_calloc(arena, block->successors_count, sizeof(int));
ZEND_HASH_FOREACH_VAL(jumptable, zv) {
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, Z_LVAL_P(zv))];
} ZEND_HASH_FOREACH_END();
block->successors[s++] = block_map[ZEND_OFFSET_TO_OPLINE_NUM(op_array, opline, opline->extended_value)];
block->successors[s++] = j + 1;
break;
}
default:
block->successors_count = 1;
block->successors[0] = j + 1;
break;
}
}
/* Build CFG, Step 4, Mark Reachable Basic Blocks */
cfg->flags |= flags;
zend_mark_reachable_blocks(op_array, cfg, 0);
return SUCCESS;
}
/* }}} */
/* }}} */
pcov-1.0.12/cfg/704/zend_worklist.h 0000664 0001750 0001750 00000007513 14724013203 015340 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Andy Wingo |
+----------------------------------------------------------------------+
*/
#ifndef _ZEND_WORKLIST_H_
#define _ZEND_WORKLIST_H_
#include "zend_arena.h"
#include "zend_bitset.h"
typedef struct _zend_worklist_stack {
int *buf;
int len;
int capacity;
} zend_worklist_stack;
#define ZEND_WORKLIST_STACK_ALLOCA(s, _len, use_heap) do { \
(s)->buf = (int*)do_alloca(sizeof(int) * _len, use_heap); \
(s)->len = 0; \
(s)->capacity = _len; \
} while (0)
#define ZEND_WORKLIST_STACK_FREE_ALLOCA(s, use_heap) \
free_alloca((s)->buf, use_heap)
static inline int zend_worklist_stack_prepare(zend_arena **arena, zend_worklist_stack *stack, int len)
{
ZEND_ASSERT(len >= 0);
stack->buf = (int*)zend_arena_calloc(arena, sizeof(*stack->buf), len);
stack->len = 0;
stack->capacity = len;
return SUCCESS;
}
static inline void zend_worklist_stack_push(zend_worklist_stack *stack, int i)
{
ZEND_ASSERT(stack->len < stack->capacity);
stack->buf[stack->len++] = i;
}
static inline int zend_worklist_stack_peek(zend_worklist_stack *stack)
{
ZEND_ASSERT(stack->len);
return stack->buf[stack->len - 1];
}
static inline int zend_worklist_stack_pop(zend_worklist_stack *stack)
{
ZEND_ASSERT(stack->len);
return stack->buf[--stack->len];
}
typedef struct _zend_worklist {
zend_bitset visited;
zend_worklist_stack stack;
} zend_worklist;
#define ZEND_WORKLIST_ALLOCA(w, _len, use_heap) do { \
(w)->stack.buf = (int*)do_alloca(ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len) + sizeof(zend_ulong) * zend_bitset_len(_len), use_heap); \
(w)->stack.len = 0; \
(w)->stack.capacity = _len; \
(w)->visited = (zend_bitset)((char*)(w)->stack.buf + ZEND_MM_ALIGNED_SIZE(sizeof(int) * _len)); \
memset((w)->visited, 0, sizeof(zend_ulong) * zend_bitset_len(_len)); \
} while (0)
#define ZEND_WORKLIST_FREE_ALLOCA(w, use_heap) \
free_alloca((w)->stack.buf, use_heap)
static inline int zend_worklist_prepare(zend_arena **arena, zend_worklist *worklist, int len)
{
ZEND_ASSERT(len >= 0);
worklist->visited = (zend_bitset)zend_arena_calloc(arena, sizeof(zend_ulong), zend_bitset_len(len));
return zend_worklist_stack_prepare(arena, &worklist->stack, len);
}
static inline int zend_worklist_len(zend_worklist *worklist)
{
return worklist->stack.len;
}
static inline int zend_worklist_push(zend_worklist *worklist, int i)
{
ZEND_ASSERT(i >= 0 && i < worklist->stack.capacity);
if (zend_bitset_in(worklist->visited, i)) {
return 0;
}
zend_bitset_incl(worklist->visited, i);
zend_worklist_stack_push(&worklist->stack, i);
return 1;
}
static inline int zend_worklist_peek(zend_worklist *worklist)
{
return zend_worklist_stack_peek(&worklist->stack);
}
static inline int zend_worklist_pop(zend_worklist *worklist)
{
/* Does not clear visited flag */
return zend_worklist_stack_pop(&worklist->stack);
}
#endif /* _ZEND_WORKLIST_H_ */
pcov-1.0.12/LICENSE 0000664 0001750 0001750 00000006204 14724013203 012221 0 ustar remi remi --------------------------------------------------------------------
The PHP License, version 3.01
Copyright (c) 1999 - 2018 The PHP Group. All rights reserved.
--------------------------------------------------------------------
Redistribution and use in source and binary forms, with or without
modification, is permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. The name "PHP" must not be used to endorse or promote products
derived from this software without prior written permission. For
written permission, please contact group@php.net.
4. Products derived from this software may not be called "PHP", nor
may "PHP" appear in their name, without prior written permission
from group@php.net. You may indicate that your software works in
conjunction with PHP by saying "Foo for PHP" instead of calling
it "PHP Foo" or "phpfoo"
5. The PHP Group may publish revised and/or new versions of the
license from time to time. Each version will be given a
distinguishing version number.
Once covered code has been published under a particular version
of the license, you may always continue to use it under the terms
of that version. You may also choose to use such covered code
under the terms of any subsequent version of the license
published by the PHP Group. No one other than the PHP Group has
the right to modify the terms applicable to covered code created
under this License.
6. Redistributions of any form whatsoever must retain the following
acknowledgment:
"This product includes PHP software, freely available from
".
THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
OF THE POSSIBILITY OF SUCH DAMAGE.
--------------------------------------------------------------------
This software consists of voluntary contributions made by many
individuals on behalf of the PHP Group.
The PHP Group can be contacted via Email at group@php.net.
For more information on the PHP Group and the PHP project,
please see .
PHP includes the Zend Engine, freely available at
.
pcov-1.0.12/README.md 0000664 0001750 0001750 00000015353 14724013203 012500 0 ustar remi remi PCOV
====
[](https://travis-ci.org/krakjoe/pcov)
[](https://ci.appveyor.com/project/krakjoe/pcov)
A self contained [CodeCoverage](https://github.com/sebastianbergmann/php-code-coverage) compatible driver for PHP
Requirements and Installation
=============================
See [INSTALL.md](INSTALL.md)
API
===
```php
/**
* Shall start recording coverage information
*/
function \pcov\start() : void;
/**
* Shall stop recording coverage information
*/
function \pcov\stop() : void;
/**
* Shall collect coverage information
*
* @param integer $type define witch type of information should be collected
* \pcov\all shall collect coverage information for all files
* \pcov\inclusive shall collect coverage information for the specified files
* \pcov\exclusive shall collect coverage information for all but the specified files
* @param array $filter path of files (realpath) that should be filtered
*
* @return array
*/
function \pcov\collect(int $type = \pcov\all, array $filter = []) : array;
/**
* Shall clear stored information
*
* @param bool $files set true to clear file tables
*
* Note: clearing the file tables may have surprising consequences
*/
function \pcov\clear(bool $files = false) : void;
/**
* Shall return list of files waiting to be collected
*/
function \pcov\waiting() : array;
/**
* Shall return the current size of the trace and cfg arena
*/
function \pcov\memory() : int;
```
Configuration
=============
PCOV is configured using PHP.ini:
| Option | Default | Changeable | Description |
|:-----------------------|:-------------------|:--------------:|:------------------------------------------------------|
| `pcov.enabled` | 1 | SYSTEM | enable or disable zend hooks for pcov |
| `pcov.directory` | auto | SYSTEM,PERDIR | restrict collection to files under this path |
| `pcov.exclude` | unused | SYSTEM,PERDIR | exclude files under pcov.directory matching this PCRE |
| `pcov.initial.memory` | 65536 | SYSTEM,PERDIR | shall set initial size of arena |
| `pcov.initial.files` | 64 | SYSTEM,PERDIR | shall set initial size of tables |
Notes
-----
The recommended defaults for production should be:
* `pcov.enabled = 0`
The recommended defaults for development should be:
* `pcov.enabled = 1`
* `pcov.directory = /path/to/your/source/directory`
When `pcov.directory` is left unset, PCOV will attempt to find `src`, `lib` or, `app` in the current
working directory, in that order; If none are found the current directory will be used, which may waste resources storing
coverage information for the test suite.
If `pcov.directory` contains test code, it's recommended to set `pcov.exclude` to avoid wasting resources.
To avoid unnecessary allocation of additional arenas for traces and control flow graphs, `pcov.initial.memory` should be set according to the memory required by the test suite, which may be discovered with `\pcov\memory()`.
To avoid reallocation of tables, `pcov.initial.files` should be set to a number higher than the number of files that will be loaded during testing, inclusive of test files.
*Note that arenas are allocated in chunks: If the chunk size is set to 65536 and pcov require 65537 bytes, the system will allocate two chunks, each 65536 bytes. When setting arena space therefore, be generous in your estimates.*
Interoperability
================
When PCOV is enabled by configuration `pcov.enabled=1`:
* interoperability with Xdebug is not possible
* interoperability with phpdbg is not possible
* interoperability with Blackfire profiler is not possible
At an internals level, the executor function is overriden by pcov, so any extension or SAPI which does the same will be broken.
When PCOV is disabled by configuration `pcov.enabled=0`:
* PCOV is zero cost - code runs at full speed
* Xdebug may be loaded
* phpdbg may be executed
* Blackfire probe may be loaded
At an internals level, the executor function is untouched, and pcov allocates no memory.
Differences in Reporting
========================
There are subtle differences between Xdebug and PCOV in reporting: Both Xdebug and PCOV perform branch analysis in order to detect executable code. Xdebug has custom written (very mature, proven) analysis, while PCOV uses the very well proven control flow graph from Optimizer. They generate comparably accurate reports, while phpdbg uses less robust detection of executable code and generates reports with known defects. One such defect in phpdbg is this:
```php
/* 2 */ function foo($bar) {
/* 3 */ if ($bar) {
/* 4 */ return true;
/* 5 */ }
/* 6 */ }
```
phpdbg will detect that this function is 100% covered when the first control path is taken, `if ($bar)`, because it cannot correctly detect which implicit return paths inserted by Zend at compile time are executable, and so chooses to ignore them all. While this may seem like a trivial difference to some, it means that the reports generated by phpdbg are not completely trustworthy.
While the accuracy of Xdebug and PCOV are comparable, the reports they generate are not precisely the same, one such example is the `switch` construct:
```php
/* 2 */ switch ($condition) {
/* 3 */ case 1:
/* 4 */ return "PHP rox!";
/* 5 */ }
```
From PHP 7.2.15 and PCOV 1.0, PCOV will detect the executability of the cases inside the switch body correctly, but will not detect line 2 (with the `switch` statement) as executable because Zend didn't output an executable opcode on that line. Xdebug's custom analysis doesn't use the same method and so will show an extra executable line on 2. Pre 7.2.15 and PCOV 1.0, the coverage of some switches is questionable as a result of the way Zend constructs the opcodes in the body of the switch - it may not execute them depending on a jump table optimization.
While Xdebug and PCOV both do the same kind of analysis of code, Xdebug is currently able to do more with that information than just generate accurate line coverage reports, it has path coverage and PCOV does not, although path coverage is not yet implemented (and probably won't be) by CodeCoverage.
Differences in Performance
==========================
The differences in performance of Xdebug and PCOV are not slight. Xdebug is first and foremost a debugging extension, and when you load it, you incur the overhead of a debugger even when it's disabled. PCOV is less than 1000 lines of code (not including CFG) and doesn't have anything like the overhead of a debugger.
pcov-1.0.12/config.m4 0000664 0001750 0001750 00000002176 14724013203 012727 0 ustar remi remi dnl $Id$
dnl config.m4 for extension pcov
PHP_ARG_ENABLE(pcov, whether to enable php coverage support,
[ --enable-pcov Enable php coverage support])
if test "$PHP_PCOV" != "no"; then
PHP_VERSION=$($PHP_CONFIG --vernum)
AC_MSG_CHECKING(PHP version)
if test $PHP_VERSION -gt 80099; then
PHP_NEW_EXTENSION(pcov, pcov.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
else
if test $PHP_VERSION -lt 70100; then
AC_MSG_ERROR([pcov supports PHP 7.1+])
elif test $PHP_VERSION -lt 70200; then
AC_MSG_RESULT(7.1)
PHP_PCOV_CFG_VERSION=701
elif test $PHP_VERSION -lt 70300; then
AC_MSG_RESULT(7.2)
PHP_PCOV_CFG_VERSION=702
elif test $PHP_VERSION -lt 70400; then
AC_MSG_RESULT(7.3)
PHP_PCOV_CFG_VERSION=703
else
AC_MSG_RESULT(7.4+)
PHP_PCOV_CFG_VERSION=704
fi
PHP_NEW_EXTENSION(pcov, pcov.c cfg/$PHP_PCOV_CFG_VERSION/zend_cfg.c, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
PHP_ADD_BUILD_DIR($ext_builddir/cfg/$PHP_PCOV_CFG_VERSION, 1)
PHP_ADD_INCLUDE($ext_srcdir/cfg/$PHP_PCOV_CFG_VERSION)
fi
fi
pcov-1.0.12/config.w32 0000664 0001750 0001750 00000001433 14724013203 013015 0 ustar remi remi // $Id$
// vim:ft=javascript
ARG_ENABLE("pcov", "enable php coverage support", "no");
if (PHP_PCOV != "no") {
if (PHP_VERSION < 7 || PHP_VERSION == 7 && PHP_MINOR_VERSION < 1) {
ERROR("pcov supports PHP 7.1+");
}
if (PHP_VERSION > 8 || PHP_VERSION == 8 && PHP_MINOR_VERSION >= 1) {
EXTENSION("pcov", "pcov.c", PHP_PCOV_SHARED,
"/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
} else {
var cfg_dir;
if (PHP_VERSION == 7 && PHP_MINOR_VERSION < 4) {
cfg_dir = PHP_VERSION + "0" + PHP_MINOR_VERSION;
} else {
cfg_dir = "704";
}
EXTENSION("pcov", "pcov.c", PHP_PCOV_SHARED,
"/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /I" +
configure_module_dirname + "/cfg/" + cfg_dir);
ADD_SOURCES(
configure_module_dirname + "/cfg/" + cfg_dir,
"zend_cfg.c",
"pcov"
);
}
}
pcov-1.0.12/php_pcov.h 0000644 0001750 0001750 00000004503 14724013203 013201 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: krakjoe |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifndef PHP_PCOV_H
#define PHP_PCOV_H
extern zend_module_entry pcov_module_entry;
#define phpext_pcov_ptr &pcov_module_entry
#define PHP_PCOV_VERSION "1.0.12"
#ifdef PHP_WIN32
# define PHP_PCOV_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
# define PHP_PCOV_API __attribute__ ((visibility("default")))
#else
# define PHP_PCOV_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
typedef struct _php_coverage_t php_coverage_t;
struct _php_coverage_t {
zend_string *file;
uint32_t line;
php_coverage_t *next;
};
ZEND_BEGIN_MODULE_GLOBALS(pcov)
zend_bool enabled;
zend_arena *mem;
php_coverage_t *start;
php_coverage_t **next;
php_coverage_t **last;
HashTable waiting;
HashTable files;
HashTable ignores;
HashTable wants;
HashTable discovered;
HashTable covered;
zend_string *directory;
pcre_cache_entry *exclude;
struct {
zend_bool enabled;
zend_long memory;
zend_long files;
char *directory;
char *exclude;
} ini;
ZEND_END_MODULE_GLOBALS(pcov)
#define PCG(v) ZEND_MODULE_GLOBALS_ACCESSOR(pcov, v)
#if defined(ZTS) && defined(COMPILE_DL_PCOV)
ZEND_TSRMLS_CACHE_EXTERN()
#endif
#endif /* PHP_PCOV_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/
pcov-1.0.12/pcov.c 0000644 0001750 0001750 00000053354 14724013203 012335 0 ustar remi remi /*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Author: krakjoe |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/pcre/php_pcre.h"
#include "zend_arena.h"
#if PHP_VERSION_ID < 80100
# include "zend_cfg.h"
# define PHP_PCOV_CFG ZEND_RT_CONSTANTS
#else
# include "Zend/Optimizer/zend_cfg.h"
# define PHP_PCOV_CFG 0
#endif
#include "zend_exceptions.h"
#include "zend_vm.h"
#include "zend_vm_opcodes.h"
#include "php_pcov.h"
#define PCOV_FILTER_ALL 0
#define PCOV_FILTER_INCLUDE 1
#define PCOV_FILTER_EXCLUDE 2
#define PHP_PCOV_UNCOVERED -1
#define PHP_PCOV_COVERED 1
#ifndef GC_ADDREF
# define GC_ADDREF(g) ++GC_REFCOUNT(g)
#endif
#if PHP_VERSION_ID < 70300
#define php_pcre_pce_incref(c) (c)->refcount++
#define php_pcre_pce_decref(c) (c)->refcount--
#define GC_SET_REFCOUNT(ref, rc) (GC_REFCOUNT(ref) = (rc))
#endif
#define PHP_PCOV_API_ENABLED_GUARD() do { \
if (!INI_BOOL("pcov.enabled")) { \
return; \
} \
} while (0);
static zval php_pcov_uncovered;
static zval php_pcov_covered;
void (*zend_execute_ex_function)(zend_execute_data *execute_data);
zend_op_array* (*zend_compile_file_function)(zend_file_handle *fh, int type) = NULL;
ZEND_DECLARE_MODULE_GLOBALS(pcov)
PHP_INI_BEGIN()
STD_PHP_INI_BOOLEAN(
"pcov.enabled", "1",
PHP_INI_SYSTEM, OnUpdateBool,
ini.enabled, zend_pcov_globals, pcov_globals)
STD_PHP_INI_ENTRY (
"pcov.directory", "",
PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString,
ini.directory, zend_pcov_globals, pcov_globals)
STD_PHP_INI_ENTRY (
"pcov.exclude", "",
PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateString,
ini.exclude, zend_pcov_globals, pcov_globals)
STD_PHP_INI_ENTRY(
"pcov.initial.memory", "65336",
PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateLong,
ini.memory, zend_pcov_globals, pcov_globals)
STD_PHP_INI_ENTRY(
"pcov.initial.files", "64",
PHP_INI_SYSTEM | PHP_INI_PERDIR, OnUpdateLong,
ini.files, zend_pcov_globals, pcov_globals)
PHP_INI_END()
static PHP_GINIT_FUNCTION(pcov)
{
#if defined(COMPILE_DL_PCOV) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
ZEND_SECURE_ZERO(pcov_globals, sizeof(zend_pcov_globals));
}
static zend_always_inline zend_bool php_pcov_wants(zend_string *filename) { /* {{{ */
if (!PCG(directory)) {
return 1;
}
if (ZSTR_LEN(filename) < ZSTR_LEN(PCG(directory))) {
return 0;
}
if (zend_hash_exists(&PCG(wants), filename)) {
return 1;
}
if (zend_hash_exists(&PCG(ignores), filename)) {
return 0;
}
if (strncmp(
ZSTR_VAL(filename),
ZSTR_VAL(PCG(directory)),
ZSTR_LEN(PCG(directory))) == SUCCESS) {
if (PCG(exclude)) {
zval match;
ZVAL_UNDEF(&match);
php_pcre_match_impl(
PCG(exclude),
#if PHP_VERSION_ID >= 70400
filename,
#else
ZSTR_VAL(filename), ZSTR_LEN(filename),
#endif
&match, NULL,
#if PHP_VERSION_ID >= 80400
false, 0, 0);
#else
0, 0, 0, 0);
#endif
if (zend_is_true(&match)) {
zend_hash_add_empty_element(
&PCG(ignores), filename);
return 0;
}
}
zend_hash_add_empty_element(&PCG(wants), filename);
return 1;
}
zend_hash_add_empty_element(&PCG(ignores), filename);
return 0;
} /* }}} */
static zend_always_inline zend_bool php_pcov_ignored_opcode(zend_uchar opcode) { /* {{{ */
return
opcode == ZEND_NOP ||
opcode == ZEND_OP_DATA ||
opcode == ZEND_FE_FREE ||
opcode == ZEND_FREE ||
opcode == ZEND_ASSERT_CHECK ||
opcode == ZEND_VERIFY_RETURN_TYPE ||
opcode == ZEND_RECV ||
opcode == ZEND_RECV_INIT ||
opcode == ZEND_RECV_VARIADIC ||
opcode == ZEND_SEND_VAL ||
opcode == ZEND_SEND_VAR_EX ||
opcode == ZEND_SEND_REF ||
opcode == ZEND_SEND_UNPACK ||
opcode == ZEND_DECLARE_CONST ||
opcode == ZEND_DECLARE_CLASS ||
#ifdef ZEND_DECLARE_INHERITED_CLASS
opcode == ZEND_DECLARE_INHERITED_CLASS ||
opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED ||
opcode == ZEND_DECLARE_ANON_INHERITED_CLASS ||
#else
opcode == ZEND_DECLARE_CLASS_DELAYED ||
#endif
opcode == ZEND_DECLARE_FUNCTION ||
opcode == ZEND_DECLARE_ANON_CLASS ||
opcode == ZEND_FAST_RET ||
opcode == ZEND_FAST_CALL ||
opcode == ZEND_TICKS ||
opcode == ZEND_EXT_STMT ||
opcode == ZEND_EXT_FCALL_BEGIN ||
opcode == ZEND_EXT_FCALL_END ||
opcode == ZEND_EXT_NOP ||
#if PHP_VERSION_ID < 70400
opcode == ZEND_VERIFY_ABSTRACT_CLASS ||
opcode == ZEND_ADD_TRAIT ||
opcode == ZEND_BIND_TRAITS ||
#endif
opcode == ZEND_BIND_GLOBAL
;
} /* }}} */
static zend_always_inline zend_string* php_pcov_interned_string(zend_string *string) { /* {{{ */
if (ZSTR_IS_INTERNED(string)) {
return string;
}
return zend_new_interned_string(zend_string_copy(string));
} /* }}} */
static zend_always_inline php_coverage_t* php_pcov_create(zend_execute_data *execute_data) { /* {{{ */
php_coverage_t *create = (php_coverage_t*) zend_arena_alloc(&PCG(mem), sizeof(php_coverage_t));
create->file = php_pcov_interned_string(EX(func)->op_array.filename);
create->line = EX(opline)->lineno;
create->next = NULL;
zend_hash_add_empty_element(&PCG(waiting), create->file);
return create;
} /* }}} */
static zend_always_inline int php_pcov_has(zend_string *filename, uint32_t lineno) { /* {{{ */
HashTable *table = zend_hash_find_ptr(&PCG(covered), filename);
if (UNEXPECTED(!table)) {
HashTable covering;
zend_hash_init(&covering, 64, NULL, NULL, 0);
table = zend_hash_add_mem(
&PCG(covered), filename, &covering, sizeof(HashTable));
zend_hash_index_add_empty_element(table, lineno);
return 0;
}
if (EXPECTED(zend_hash_index_exists(table, lineno))) {
return 1;
}
zend_hash_index_add_empty_element(table, lineno);
return 0;
} /* }}} */
static zend_always_inline int php_pcov_trace(zend_execute_data *execute_data) { /* {{{ */
if (PCG(enabled)) {
if (php_pcov_wants(EX(func)->op_array.filename) &&
!php_pcov_ignored_opcode(EX(opline)->opcode) &&
!php_pcov_has(EX(func)->op_array.filename, EX(opline)->lineno)) {
php_coverage_t *coverage = php_pcov_create(execute_data);
if (!PCG(start)) {
PCG(start) = coverage;
} else {
*(PCG(next)) = coverage;
}
PCG(next) = &coverage->next;
}
}
return zend_vm_call_opcode_handler(execute_data);
} /* }}} */
zend_op_array* php_pcov_compile_file(zend_file_handle *fh, int type) { /* {{{ */
zend_op_array *result = zend_compile_file_function(fh, type), *mem;
if (!result || !result->filename || !php_pcov_wants(result->filename)) {
return result;
}
if (zend_hash_exists(&PCG(files), result->filename)) {
return result;
}
mem = zend_hash_add_mem(
&PCG(files),
result->filename,
result, sizeof(zend_op_array));
#if PHP_VERSION_ID >= 70400
if (result->refcount) {
(*result->refcount)++;
}
if (result->static_variables) {
if (!(GC_FLAGS(result->static_variables) & IS_ARRAY_IMMUTABLE)) {
GC_ADDREF(result->static_variables);
}
}
mem->fn_flags &= ~ZEND_ACC_HEAP_RT_CACHE;
#else
(void)mem;
function_add_ref((zend_function*)result);
#endif
return result;
} /* }}} */
void php_pcov_execute_ex(zend_execute_data *execute_data) { /* {{{ */
int zrc = 0;
while (1) {
zrc = php_pcov_trace(execute_data);
if (zrc != SUCCESS) {
if (zrc < SUCCESS) {
return;
}
execute_data = EG(current_execute_data);
}
}
} /* }}} */
void php_pcov_covered_dtor(zval *zv) { /* {{{ */
zend_hash_destroy(Z_PTR_P(zv));
efree(Z_PTR_P(zv));
} /* }}} */
void php_pcov_files_dtor(zval *zv) { /* {{{ */
destroy_op_array(Z_PTR_P(zv));
efree(Z_PTR_P(zv));
} /* }}} */
void php_pcov_filename_dtor(zval *zv) { /* {{{ */
free(Z_PTR_P(zv));
} /* }}} */
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(pcov)
{
REGISTER_NS_LONG_CONSTANT("pcov", "all", PCOV_FILTER_ALL, CONST_CS|CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("pcov", "inclusive", PCOV_FILTER_INCLUDE, CONST_CS|CONST_PERSISTENT);
REGISTER_NS_LONG_CONSTANT("pcov", "exclusive", PCOV_FILTER_EXCLUDE, CONST_CS|CONST_PERSISTENT);
REGISTER_NS_STRING_CONSTANT("pcov", "version", PHP_PCOV_VERSION, CONST_CS|CONST_PERSISTENT);
REGISTER_INI_ENTRIES();
if (INI_BOOL("pcov.enabled")) {
zend_execute_ex_function = zend_execute_ex;
zend_execute_ex = php_pcov_execute_ex;
}
ZVAL_LONG(&php_pcov_uncovered, PHP_PCOV_UNCOVERED);
ZVAL_LONG(&php_pcov_covered, PHP_PCOV_COVERED);
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(pcov)
{
if (INI_BOOL("pcov.enabled")) {
zend_execute_ex = zend_execute_ex_function;
}
UNREGISTER_INI_ENTRIES();
return SUCCESS;
}
/* }}} */
const char *php_pcov_directory_defaults[] = { /* {{{ */
"src",
"lib",
"app",
".",
NULL
}; /* }}} */
static void php_pcov_setup_directory(char *directory) { /* {{{ */
char realpath[MAXPATHLEN];
zend_stat_t statbuf;
if (!directory || !*directory) {
const char** try = php_pcov_directory_defaults;
while (*try) {
if (VCWD_REALPATH(*try, realpath) &&
VCWD_STAT(realpath, &statbuf) == SUCCESS) {
directory = realpath;
break;
}
try++;
}
} else {
if (VCWD_REALPATH(directory, realpath) &&
VCWD_STAT(realpath, &statbuf) == SUCCESS) {
directory = realpath;
}
}
PCG(directory) = zend_string_init(directory, strlen(directory), 0);
} /* }}} */
static zend_always_inline void php_pcov_setup_exclude(char *exclude) { /* {{{ */
zend_string *pattern;
if (!exclude || !*exclude) {
return;
}
pattern = zend_string_init(
exclude, strlen(exclude), 0);
PCG(exclude) = pcre_get_compiled_regex_cache(pattern);
if (PCG(exclude)) {
php_pcre_pce_incref(PCG(exclude));
}
zend_string_release(pattern);
} /* }}} */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(pcov)
{
#if defined(COMPILE_DL_PCOV) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
if (!INI_BOOL("pcov.enabled")) {
return SUCCESS;
}
PCG(mem) = zend_arena_create(INI_INT("pcov.initial.memory"));
zend_hash_init(&PCG(files), INI_INT("pcov.initial.files"), NULL, php_pcov_files_dtor, 0);
zend_hash_init(&PCG(waiting), INI_INT("pcov.initial.files"), NULL, NULL, 0);
zend_hash_init(&PCG(ignores), INI_INT("pcov.initial.files"), NULL, NULL, 0);
zend_hash_init(&PCG(wants), INI_INT("pcov.initial.files"), NULL, NULL, 0);
zend_hash_init(&PCG(discovered), INI_INT("pcov.initial.files"), NULL, ZVAL_PTR_DTOR, 0);
zend_hash_init(&PCG(covered), INI_INT("pcov.initial.files"), NULL, php_pcov_covered_dtor, 0);
php_pcov_setup_directory(INI_STR("pcov.directory"));
php_pcov_setup_exclude(INI_STR("pcov.exclude"));
#ifdef ZEND_COMPILE_NO_JUMPTABLES
CG(compiler_options) |= ZEND_COMPILE_NO_JUMPTABLES;
#endif
if (!zend_compile_file_function) {
zend_compile_file_function = zend_compile_file;
zend_compile_file = php_pcov_compile_file;
}
PCG(start) = NULL;
PCG(last) = NULL;
PCG(next) = NULL;
return SUCCESS;
}
/* }}} */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(pcov)
{
if (!INI_BOOL("pcov.enabled") || CG(unclean_shutdown)) {
return SUCCESS;
}
zend_hash_destroy(&PCG(files));
zend_hash_destroy(&PCG(ignores));
zend_hash_destroy(&PCG(wants));
zend_hash_destroy(&PCG(discovered));
zend_hash_destroy(&PCG(waiting));
zend_hash_destroy(&PCG(covered));
zend_arena_destroy(PCG(mem));
if (PCG(directory)) {
zend_string_release(PCG(directory));
}
if (PCG(exclude)) {
php_pcre_pce_decref(PCG(exclude));
}
if (zend_compile_file == php_pcov_compile_file) {
zend_compile_file = zend_compile_file_function;
zend_compile_file_function = NULL;
}
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(pcov)
{
char info[64];
char *directory = INI_STR("pcov.directory");
char *exclude = INI_STR("pcov.exclude");
php_info_print_table_start();
php_info_print_table_header(2,
"PCOV support",
INI_BOOL("pcov.enabled") ? "Enabled" : "Disabled");
php_info_print_table_row(2,
"PCOV version",
PHP_PCOV_VERSION);
php_info_print_table_row(2,
"pcov.directory",
directory && *directory ? directory : (PCG(directory) ? ZSTR_VAL(PCG(directory)) : "auto"));
php_info_print_table_row(2,
"pcov.exclude",
exclude && *exclude ? exclude : "none" );
snprintf(info, sizeof(info),
ZEND_LONG_FMT " bytes",
(zend_long) INI_INT("pcov.initial.memory"));
php_info_print_table_row(2,
"pcov.initial.memory", info);
snprintf(info, sizeof(info),
ZEND_LONG_FMT,
(zend_long) INI_INT("pcov.initial.files"));
php_info_print_table_row(2,
"pcov.initial.files", info);
php_info_print_table_end();
}
/* }}} */
static zend_always_inline void php_pcov_report(php_coverage_t *coverage, zval *filter) { /* {{{ */
zval *table;
zval *hit;
if (!coverage) {
return;
}
do {
if ((table = zend_hash_find(Z_ARRVAL_P(filter), coverage->file))) {
if ((hit = zend_hash_index_find(Z_ARRVAL_P(table), coverage->line))) {
Z_LVAL_P(hit) = PHP_PCOV_COVERED;
}
}
} while ((coverage = coverage->next));
} /* }}} */
static void php_pcov_discover_code(zend_arena **arena, zend_op_array *ops, zval *return_value) { /* {{{ */
zend_cfg cfg;
zend_basic_block *block;
zend_op *limit = ops->opcodes + ops->last;
int i = 0;
if (ops->fn_flags & ZEND_ACC_ABSTRACT) {
return;
}
memset(&cfg, 0, sizeof(zend_cfg));
zend_build_cfg(arena, ops, PHP_PCOV_CFG, &cfg);
for (block = cfg.blocks, i = 0; i < cfg.blocks_count; i++, block++) {
zend_op *opline = ops->opcodes + block->start,
*end = opline + block->len;
if (!(block->flags & ZEND_BB_REACHABLE)) {
/*
* Note that, we don't care about unreachable blocks
* that would be removed by opcache, because it would
* create different reports depending on configuration
*/
continue;
}
while(opline < end) {
if (php_pcov_ignored_opcode(opline->opcode)) {
opline++;
continue;
}
if (!zend_hash_index_exists(Z_ARRVAL_P(return_value), opline->lineno)) {
zend_hash_index_add(
Z_ARRVAL_P(return_value),
opline->lineno, &php_pcov_uncovered);
}
if ((opline +0)->opcode == ZEND_NEW &&
(opline +1)->opcode == ZEND_DO_FCALL) {
opline++;
}
opline++;
}
if (block == cfg.blocks && opline == limit) {
/*
* If the first basic block finishes at the end of the op array
* then we don't care about subsequent blocks
*/
break;
}
}
#if PHP_VERSION_ID >= 80100
for (uint32_t def = 0; def < ops->num_dynamic_func_defs; def++) {
php_pcov_discover_code(arena, ops->dynamic_func_defs[def], return_value);
}
#endif
} /* }}} */
static void php_pcov_discover_file(zend_string *file, zval *return_value) { /* {{{ */
zval discovered;
zend_op_array *ops;
zval *cache = zend_hash_find(&PCG(discovered), file);
zend_arena *mem;
if (cache) {
zval uncached;
ZVAL_DUP(&uncached, cache);
zend_hash_update(Z_ARRVAL_P(return_value), file, &uncached);
return;
}
if (!(ops = zend_hash_find_ptr(&PCG(files), file))) {
return;
}
array_init(&discovered);
mem = zend_arena_create(1024 * 1024);
php_pcov_discover_code(&mem, ops, &discovered);
{
zend_class_entry *ce;
zend_op_array *function;
ZEND_HASH_FOREACH_PTR(EG(class_table), ce) {
if (ce->type != ZEND_USER_CLASS) {
continue;
}
ZEND_HASH_FOREACH_PTR(&ce->function_table, function) {
if (function->type == ZEND_USER_FUNCTION &&
function->filename &&
zend_string_equals(file, function->filename)) {
php_pcov_discover_code(&mem, function, &discovered);
}
} ZEND_HASH_FOREACH_END();
} ZEND_HASH_FOREACH_END();
}
{
zend_op_array *function;
ZEND_HASH_FOREACH_PTR(EG(function_table), function) {
if (function->type == ZEND_USER_FUNCTION &&
function->filename &&
zend_string_equals(file, function->filename)) {
php_pcov_discover_code(&mem, function, &discovered);
}
} ZEND_HASH_FOREACH_END();
}
zend_hash_update(&PCG(discovered), file, &discovered);
zend_arena_destroy(mem);
php_pcov_discover_file(file, return_value);
} /* }}} */
static zend_always_inline void php_pcov_clean(HashTable *table) { /* {{{ */
if (table->nNumUsed) {
zend_hash_clean(table);
}
} /* }}} */
/* {{{ array \pcov\collect(int $type = \pcov\all, array $filter = []); */
PHP_NAMED_FUNCTION(php_pcov_collect)
{
zend_long type = PCOV_FILTER_ALL;
zval *filter = NULL;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|la", &type, &filter) != SUCCESS) {
return;
}
PHP_PCOV_API_ENABLED_GUARD();
if (PCOV_FILTER_ALL != type &&
PCOV_FILTER_INCLUDE != type &&
PCOV_FILTER_EXCLUDE != type) {
zend_throw_error(zend_ce_type_error,
"type must be "
"\\pcov\\inclusive, "
"\\pcov\\exclusive, or \\pcov\\all");
return;
}
array_init(return_value);
if (PCG(last) == PCG(next)) {
return;
}
PCG(last) = PCG(next);
switch(type) {
case PCOV_FILTER_INCLUDE: {
zval *filtered;
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(filter), filtered) {
if (Z_TYPE_P(filtered) != IS_STRING) {
continue;
}
php_pcov_discover_file(Z_STR_P(filtered), return_value);
} ZEND_HASH_FOREACH_END();
} break;
case PCOV_FILTER_EXCLUDE: {
zend_string *name;
zval *filtered;
ZEND_HASH_FOREACH_STR_KEY(&PCG(files), name) {
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(filter), filtered) {
if (Z_TYPE_P(filtered) != IS_STRING) {
continue;
}
if (zend_string_equals(name, Z_STR_P(filtered))) {
goto _php_pcov_collect_exclude;
}
} ZEND_HASH_FOREACH_END();
php_pcov_discover_file(name, return_value);
_php_pcov_collect_exclude:
continue;
} ZEND_HASH_FOREACH_END();
} break;
case PCOV_FILTER_ALL: {
zend_string *name;
ZEND_HASH_FOREACH_STR_KEY(&PCG(files), name) {
php_pcov_discover_file(name, return_value);
} ZEND_HASH_FOREACH_END();
} break;
}
php_pcov_report(PCG(start), return_value);
} /* }}} */
/* {{{ void \pcov\start(void) */
PHP_NAMED_FUNCTION(php_pcov_start)
{
if (zend_parse_parameters_none() != SUCCESS) {
return;
}
PHP_PCOV_API_ENABLED_GUARD();
PCG(enabled) = 1;
} /* }}} */
/* {{{ void \pcov\stop(void) */
PHP_NAMED_FUNCTION(php_pcov_stop)
{
if (zend_parse_parameters_none() != SUCCESS) {
return;
}
PHP_PCOV_API_ENABLED_GUARD();
PCG(enabled) = 0;
} /* }}} */
/* {{{ void \pcov\clear(bool $files = 0) */
PHP_NAMED_FUNCTION(php_pcov_clear)
{
zend_bool files = 0;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|b", &files) != SUCCESS) {
return;
}
PHP_PCOV_API_ENABLED_GUARD();
if (files) {
php_pcov_clean(&PCG(files));
php_pcov_clean(&PCG(discovered));
}
zend_arena_destroy(PCG(mem));
PCG(mem) =
zend_arena_create(
INI_INT("pcov.initial.memory"));
PCG(start) = NULL;
PCG(last) = NULL;
PCG(next) = NULL;
php_pcov_clean(&PCG(waiting));
php_pcov_clean(&PCG(covered));
} /* }}} */
/* {{{ array \pcov\waiting(void) */
PHP_NAMED_FUNCTION(php_pcov_waiting)
{
zend_string *waiting;
if (zend_parse_parameters_none() != SUCCESS) {
return;
}
PHP_PCOV_API_ENABLED_GUARD();
array_init(return_value);
ZEND_HASH_FOREACH_STR_KEY(&PCG(waiting), waiting) {
add_next_index_str(
return_value,
zend_string_copy(waiting));
} ZEND_HASH_FOREACH_END();
} /* }}} */
/* {{{ int \pcov\memory(void) */
PHP_NAMED_FUNCTION(php_pcov_memory)
{
zend_arena *arena = PCG(mem);
if (zend_parse_parameters_none() != SUCCESS) {
return;
}
PHP_PCOV_API_ENABLED_GUARD();
ZVAL_LONG(return_value, 0);
do {
Z_LVAL_P(return_value) += (arena->end - arena->ptr);
} while ((arena = arena->prev));
} /* }}} */
/* {{{ */
ZEND_BEGIN_ARG_INFO_EX(php_pcov_collect_arginfo, 0, 0, 0)
ZEND_ARG_TYPE_INFO(0, type, IS_LONG, 0)
ZEND_ARG_TYPE_INFO(0, filter, IS_ARRAY, 0)
ZEND_END_ARG_INFO() /* }}} */
/* {{{ */
ZEND_BEGIN_ARG_INFO_EX(php_pcov_clear_arginfo, 0, 0, 0)
ZEND_ARG_TYPE_INFO(0, files, _IS_BOOL, 0)
ZEND_END_ARG_INFO() /* }}} */
/* {{{ */
ZEND_BEGIN_ARG_INFO_EX(php_pcov_no_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO() /* }}} */
/* {{{ php_pcov_functions[]
*/
const zend_function_entry php_pcov_functions[] = {
ZEND_NS_FENTRY("pcov", start, php_pcov_start, php_pcov_no_arginfo, 0)
ZEND_NS_FENTRY("pcov", stop, php_pcov_stop, php_pcov_no_arginfo, 0)
ZEND_NS_FENTRY("pcov", collect, php_pcov_collect, php_pcov_collect_arginfo, 0)
ZEND_NS_FENTRY("pcov", clear, php_pcov_clear, php_pcov_clear_arginfo, 0)
ZEND_NS_FENTRY("pcov", waiting, php_pcov_waiting, php_pcov_no_arginfo, 0)
ZEND_NS_FENTRY("pcov", memory, php_pcov_memory, php_pcov_no_arginfo, 0)
PHP_FE_END
};
/* }}} */
/* {{{ pcov_module_deps[] */
static const zend_module_dep pcov_module_deps[] = {
ZEND_MOD_REQUIRED("pcre")
ZEND_MOD_END
}; /* }}} */
/* {{{ pcov_module_entry
*/
zend_module_entry pcov_module_entry = {
STANDARD_MODULE_HEADER_EX,
NULL,
pcov_module_deps,
"pcov",
php_pcov_functions,
PHP_MINIT(pcov),
PHP_MSHUTDOWN(pcov),
PHP_RINIT(pcov),
PHP_RSHUTDOWN(pcov),
PHP_MINFO(pcov),
PHP_PCOV_VERSION,
PHP_MODULE_GLOBALS(pcov),
PHP_GINIT(pcov),
NULL,
NULL,
STANDARD_MODULE_PROPERTIES_EX
};
/* }}} */
#ifdef COMPILE_DL_PCOV
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_GET_MODULE(pcov)
#endif
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/