Skip to content

Commit 6aadeba

Browse files
committed
Add show_unexecuted option to phpdbg_end_oplog()
1 parent 378a05f commit 6aadeba

File tree

4 files changed

+144
-11
lines changed

4 files changed

+144
-11
lines changed

sapi/phpdbg/create-test.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
error_reporting(-1);
2828

2929
$phpdbg = getenv('TEST_PHPDBG_EXECUTABLE') ?: null;
30-
$pass_options = " -qbI";
30+
$pass_options = ' -qbI -n -d "error_reporting=32767" -d "display_errors=1" -d "display_startup_errors=1" -d "log_errors=0"';
3131
$file = "";
3232
$cmdargv = "";
3333

sapi/phpdbg/phpdbg.c

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -443,16 +443,54 @@ static PHP_FUNCTION(phpdbg_start_oplog)
443443
PHPDBG_G(oplog_list)->start = PHPDBG_G(oplog_cur);
444444
}
445445

446+
static void phpdbg_oplog_fill_executable(zend_op_array *op_array, HashTable *insert_ht, zend_bool by_opcode) {
447+
/* ignore RECV_* opcodes */
448+
zend_op *cur = op_array->opcodes + op_array->num_args + !!(op_array->fn_flags & ZEND_ACC_VARIADIC);
449+
zend_op *end = op_array->opcodes + op_array->last;
450+
451+
zend_long insert_idx;
452+
zval zero;
453+
ZVAL_LONG(&zero, 0);
454+
455+
/* ignore autogenerated return (well, not too precise with finally branches, but that's okay) */
456+
if (op_array->last > 1 && (end - 1)->opcode == ZEND_RETURN && ((end - 2)->opcode == ZEND_RETURN || (end - 2)->opcode == ZEND_GENERATOR_RETURN || (end - 2)->opcode == ZEND_THROW)) {
457+
end--;
458+
}
459+
460+
for (; cur < end; cur++) {
461+
if (cur->opcode == ZEND_NOP || cur->opcode == ZEND_OP_DATA || cur->opcode == ZEND_FE_FREE || cur->opcode == ZEND_FREE || cur->opcode == ZEND_ASSERT_CHECK
462+
|| cur->opcode == ZEND_DECLARE_CONST || cur->opcode == ZEND_DECLARE_CLASS || cur->opcode == ZEND_DECLARE_INHERITED_CLASS || cur->opcode == ZEND_DECLARE_FUNCTION
463+
|| cur->opcode == ZEND_DECLARE_INHERITED_CLASS_DELAYED || cur->opcode == ZEND_VERIFY_ABSTRACT_CLASS || cur->opcode == ZEND_ADD_TRAIT || cur->opcode == ZEND_BIND_TRAITS
464+
|| cur->opcode == ZEND_DECLARE_ANON_CLASS || cur->opcode == ZEND_DECLARE_ANON_INHERITED_CLASS || cur->opcode == ZEND_FAST_RET || cur->opcode == ZEND_TICKS
465+
|| cur->opcode == ZEND_EXT_STMT || cur->opcode == ZEND_EXT_FCALL_BEGIN || cur->opcode == ZEND_EXT_FCALL_END || cur->opcode == ZEND_EXT_NOP || cur->opcode == ZEND_BIND_GLOBAL) {
466+
continue;
467+
}
468+
469+
if (by_opcode) {
470+
insert_idx = cur - op_array->opcodes;
471+
} else {
472+
insert_idx = cur->lineno;
473+
}
474+
475+
if (cur->opcode == ZEND_NEW && (cur + 1)->opcode == ZEND_DO_FCALL) {
476+
cur++;
477+
}
478+
479+
zend_hash_index_update(insert_ht, insert_idx, &zero);
480+
}
481+
}
482+
446483
/* {{{ proto void phpdbg_end_oplog() */
447484
static PHP_FUNCTION(phpdbg_end_oplog)
448485
{
449-
phpdbg_oplog_entry *cur = PHPDBG_G(oplog_list)->start;
450-
phpdbg_oplog_list *prev = PHPDBG_G(oplog_list)->prev;
486+
phpdbg_oplog_entry *cur;
487+
phpdbg_oplog_list *prev;
451488

452489
HashTable *options = NULL;
453490
zval *option_buffer;
454491
zend_bool by_function = 0;
455492
zend_bool by_opcode = 0;
493+
zend_bool show_unexecuted = 0;
456494

457495
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|H", &options) == FAILURE) {
458496
return;
@@ -463,6 +501,9 @@ static PHP_FUNCTION(phpdbg_end_oplog)
463501
return;
464502
}
465503

504+
cur = PHPDBG_G(oplog_list)->start;
505+
prev = PHPDBG_G(oplog_list)->prev;
506+
466507
efree(PHPDBG_G(oplog_list));
467508
PHPDBG_G(oplog_list) = prev;
468509

@@ -476,6 +517,10 @@ static PHP_FUNCTION(phpdbg_end_oplog)
476517
}
477518
}
478519

520+
if (options && (option_buffer = zend_hash_str_find(options, ZEND_STRL("show_unexecuted")))) {
521+
show_unexecuted = zend_is_true(option_buffer);
522+
}
523+
479524
array_init(return_value);
480525

481526
{
@@ -490,35 +535,51 @@ static PHP_FUNCTION(phpdbg_end_oplog)
490535

491536
do {
492537
zend_op_array *op_array = cur->op_array;
538+
zval zero;
539+
ZVAL_LONG(&zero, 0);
540+
493541
if (op_array->filename != last_file) {
494542
last_file = op_array->filename;
495543
file_buf = zend_hash_find(Z_ARR_P(return_value), last_file);
496544
if (!file_buf) {
497545
zval ht;
498546
array_init(&ht);
499547
file_buf = zend_hash_add_new(Z_ARR_P(return_value), last_file, &ht);
548+
549+
if (show_unexecuted) {
550+
phpdbg_oplog_fill_executable(op_array, Z_ARR_P(file_buf), by_opcode);
551+
}
500552
}
553+
insert_ht = Z_ARR_P(file_buf);
501554
}
502-
insert_ht = Z_ARR_P(file_buf);
503555

504556
if (by_function) {
505-
if (op_array->function_name != last_function || op_array->scope != last_scope) {
557+
if (op_array->function_name == NULL) {
558+
if (last_function != NULL) {
559+
insert_ht = Z_ARR_P(file_buf);
560+
}
561+
last_function = NULL;
562+
} else if (op_array->function_name != last_function || op_array->scope != last_scope) {
506563
zend_string *fn_name;
507564
last_function = op_array->function_name;
508565
last_scope = op_array->scope;
509566
if (last_scope == NULL) {
510-
fn_name = zend_string_copy(last_function);
567+
fn_name = zend_string_copy(last_function ? last_function : last_file);
511568
} else {
512-
fn_name = strpprintf(ZSTR_LEN(last_function) + ZSTR_LEN(last_scope->name) + 2, "%.*s::%.*s", ZSTR_LEN(last_function), ZSTR_VAL(last_function), ZSTR_LEN(last_scope->name), ZSTR_VAL(last_scope->name));
569+
fn_name = strpprintf(ZSTR_LEN(last_function) + ZSTR_LEN(last_scope->name) + 2, "%.*s::%.*s", ZSTR_LEN(last_scope->name), ZSTR_VAL(last_scope->name), ZSTR_LEN(last_function), ZSTR_VAL(last_function));
513570
}
514571
fn_buf = zend_hash_find(Z_ARR_P(return_value), fn_name);
515572
if (!fn_buf) {
516573
zval ht;
517574
array_init(&ht);
518575
fn_buf = zend_hash_add_new(Z_ARR_P(return_value), fn_name, &ht);
576+
577+
if (show_unexecuted) {
578+
phpdbg_oplog_fill_executable(op_array, Z_ARR_P(fn_buf), by_opcode);
579+
}
519580
}
581+
insert_ht = Z_ARR_P(fn_buf);
520582
}
521-
insert_ht = Z_ARR_P(fn_buf);
522583
}
523584

524585
if (by_opcode) {
@@ -530,9 +591,7 @@ static PHP_FUNCTION(phpdbg_end_oplog)
530591
{
531592
zval *num = zend_hash_index_find(insert_ht, insert_idx);
532593
if (!num) {
533-
zval zv;
534-
ZVAL_LONG(&zv, 0);
535-
num = zend_hash_index_add_new(insert_ht, insert_idx, &zv);
594+
num = zend_hash_index_add_new(insert_ht, insert_idx, &zero);
536595
}
537596
Z_LVAL_P(num)++;
538597
}
@@ -1675,6 +1734,8 @@ int main(int argc, char **argv) /* {{{ */
16751734
goto phpdbg_out;
16761735
}
16771736

1737+
PG(during_request_startup) = 0;
1738+
16781739
phpdbg_fully_started = 1;
16791740

16801741
/* #ifndef for making compiler shutting up */
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
--TEST--
2+
Test phpdbg_*_oplog() functions
3+
--PHPDBG--
4+
r
5+
q
6+
--EXPECTF--
7+
[Successful compilation of %s]
8+
prompt> halloarray(2) {
9+
["%s"]=>
10+
array(6) {
11+
[11]=>
12+
int(0)
13+
[13]=>
14+
int(1)
15+
[17]=>
16+
int(2)
17+
[18]=>
18+
int(2)
19+
[19]=>
20+
int(3)
21+
[21]=>
22+
int(4)
23+
}
24+
["A::b"]=>
25+
array(2) {
26+
[5]=>
27+
int(5)
28+
[4]=>
29+
int(1)
30+
}
31+
}
32+
[Script ended normally]
33+
prompt>
34+
--FILE--
35+
<?php
36+
37+
class A {
38+
public function b($c = 1) {
39+
if ($c == 1) {
40+
// comment
41+
}
42+
}
43+
}
44+
45+
phpdbg_start_oplog();
46+
47+
echo "hallo";
48+
49+
// fcalls
50+
51+
$a = new A();
52+
$a->b();
53+
$a->b('ha');
54+
55+
var_dump(phpdbg_end_oplog(["functions" => true, "show_unexecuted" => true]));
56+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
--TEST--
2+
phpdbg_end_oplog() alone must not crash
3+
--PHPDBG--
4+
r
5+
q
6+
--EXPECTF--
7+
[Successful compilation of %s]
8+
prompt>
9+
Warning: Can not end an oplog without starting it in %s on line 3
10+
NULL
11+
[Script ended normally]
12+
prompt>
13+
--FILE--
14+
<?php
15+
16+
var_dump(phpdbg_end_oplog());

0 commit comments

Comments
 (0)