>  기사  >  백엔드 개발  >  GDB를 사용하여 PHP 코드 디버깅 및 PHP의 무한 루프 문제 해결 code_php 팁

GDB를 사용하여 PHP 코드 디버깅 및 PHP의 무한 루프 문제 해결 code_php 팁

WBOY
WBOY원래의
2016-05-16 20:22:261248검색

최근 동료들이 Swoole Server 문제를 해결하도록 도와주던 중 한 작업자 프로세스가 항상 R 상태에 있고 CPU 소비가 매우 높은 것을 발견했습니다. 예비 결론은 PHP 코드에서 무한 루프가 발생한다는 것입니다.

다음 코드는 PHP 무한 루프 문제를 해결하는 방법을 보여줍니다.

코드 복사 코드는 다음과 같습니다.

#dead_loop.php
$배열 = 배열();
for($i = 0; $i {
$배열[] = $i;
}
include __DIR__."/include.php";
#include.php
동안(1)
{
usleep(10);
$keys = array_flip($array);
$index = array_search(rand(1500, 9999), $array);
$str = str_repeat('A', $index);
$strb = 테스트($index, $str);
}
기능 테스트($index, $str)
{
Return str_replace('A', 'B', $str);
}

다음과 같이 ps aux를 통해 프로세스 ID 및 상태를 가져오고, gdb -p process ptrace 추적을 사용하고, bt 명령을 통해 호출 스택을 가져옵니다

코드 복사 코드는 다음과 같습니다.

htf 3834 2.6 0.2 166676 22060 pts/12 R 10:50 0:12 php dead_loop.php
gdb -p 3834
(gdb)BT
zend_mm_check_ptr의 #0 0x00000000008cc03f(힙=0x1eaa2c0, ptr=0x2584910, Silent=1, __zend_filename=0xee3d40 "/home/htf/workspace/php-5.4.27/Zend/zend_variables.c",
__zend_lineno=182, __zend_orig_filename=0xee1888 "/home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c", __zend_orig_lineno=437)
/home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:1485
_zend_mm_free_int의 #1 0x000000008cd643(힙 = 0x1eaa2c0, P = 0x2584910, __zend_filename = 0xee3d40 "/home/workspace/5.4.27/zend/VEND_V ariables.c ", __zend_lineno = 182,
__zend_orig_filename=0xee1888 "/home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c", __zend_orig_lineno=437) /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2064
#2 _efree의 0x00000000008cebf7 (ptr=0x2584910, __zend_filename=0xee3d40 "/home/htf/workspace/php-5.4.27/Zend/zend_variables.c", __zend_lineno=182,
__zend_orig_filename=0xee1888 "/home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c", __zend_orig_lineno=437) /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2436
#3 _zval_ptr_dtor의 0x00000000008eda0a (zval_ptr=0x25849a0, __zend_filename=0xee3d40 "/home/htf/workspace/php-5.4.27/Zend/zend_variables.c", __zend_lineno=182)
/home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c:437
/home/htf/workspace/php-5.4.27/Zend/zend_variables.c:182에 있는 _zval_ptr_dtor_wrapper(zval_ptr=0x25849a0)의 #4 0x00000000008fe687
#5 0x000000000091259f zend_hash_destroy(ht=0x7f7263f6e380), /home/htf/workspace/php-5.4.27/Zend/zend_hash.c:560
_zval_dtor_func의 #6 0x00000000008fe2c5 (zvalue=0x7f726426fe50, __zend_filename=0xeea290 "/home/htf/workspace/php-5.4.27/Zend/zend_execute.c", __zend_lineno=901)
/home/htf/workspace/php-5.4.27/Zend/zend_variables.c:45
_zval_dtor의 #7 0x0000000000936656 (zvalue=0x7f726426fe50, __zend_filename=0xeea290 "/home/htf/workspace/php-5.4.27/Zend/zend_execute.c", __zend_lineno=901)
/home/htf/workspace/php-5.4.27/Zend/zend_variables.h:35
/home/htf/workspace/php-5.4.27/Zend/zend_execute.c:901의 #8 0x0000000000939747 in zend_sign_to_variable (variable_ptr_ptr=0x7f7263f8e738, value=0x7f726426f6a8) /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:33168의 ZEND_ASSIGN_SPEC_CV_VAR_HANDLER(execute_data=0x7f726d04b2a8)의 #9 0x0000000000997ee5
/home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410에서 실행(op_array=0x21d58b0)의 #10 0x000000000093b5fd
/home/htf/workspace/php-5.4.27/Zend/zend.c:1315의 zend_execute_scripts(유형=8, retval=0x0, file_count=3)의 #11 0x0000000000901692
/home/htf/workspace/php-5.4.27/main/main.c:2502에 있는 php_execute_script(primary_file=0x7ffffe0038d0)의 #12 0x000000000087926a
#13 0x00000000009a32e3 in do_cli(argc=2, argv=0x7ffffe004d18) at /home/htf/workspace/php-5.4.27/sapi/cli/php_cli.c:989
#14 0x00000000009a4491(argc=2, argv=0x7ffffe004d18) /home/htf/workspace/php-5.4.27/sapi/cli/php_cli.c:1365

gdb를 실행한 후 무한 루프에 있는 프로세스는 추적 중임을 나타내는 T 상태로 변경됩니다. 이는 독점적이므로 더 이상 strace/gdb 또는 기타 ptrace 도구를 사용하여 이 프로세스를 디버깅할 수 없습니다. 또한 이 프로세스는 실행을 중단합니다. gdb가 c에 들어간 후 프로그램은 계속해서 아래쪽으로 실행됩니다. 그런 다음 Ctrl c를 다시 눌러 프로그램을 중단합니다. bt 명령을 통해 프로세스의 호출 스택을 봅니다.

코드 복사 코드는 다음과 같습니다.

(gdb)BT
#0 _zend_mm_alloc_int (힙=0x1eaa2c0, 크기=72, __zend_filename=0xe43410 "/home/htf/workspace/php-5.4.27/ext/standard/array.c", __zend_lineno=2719,
__zend_orig_filename=0xee5a38 "/home/htf/workspace/php-5.4.27/Zend/zend_hash.c", __zend_orig_lineno=412) /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:1895
#1 _emalloc의 0x00000000008ceb86(크기=72, __zend_filename=0xe43410 "/home/htf/workspace/php-5.4.27/ext/standard/array.c", __zend_lineno=2719,
__zend_orig_filename=0xee5a38 "/home/htf/workspace/php-5.4.27/Zend/zend_hash.c", __zend_orig_lineno=412) /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2425
_zend_hash_index_update_or_next_insert의 #2 0x0000000000911d85(ht=0x2257a10, h=3972, pData=0x7ffffe0012b0, nDataSize=8, pDest=0x0, 플래그=1,
__zend_filename=0xe43410 "/home/htf/workspace/php-5.4.27/ext/standard/array.c", __zend_lineno=2719) /home/htf/workspace/php-5.4.27/Zend/zend_hash.c: 412
#3 zif_array_flip의 0x00000000007767e1(ht=1, return_value=0x7f726424ea68, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1)
/home/htf/workspace/php-5.4.27/ext/standard/array.c:2719
에서 #4 0x000000000093c03e in zend_do_fcall_common_helper_SPEC(execute_data=0x7f726d04b2a8) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:643
/home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:2233에 있는 ZEND_DO_FCALL_SPEC_CONST_HANDLER(execute_data=0x7f726d04b2a8)의 #5 0x00000000009400e6
#6 /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410에서 실행(op_array=0x21d58b0)의 0x000000000093b5fd

두 차례의 BT 정보가 다른 이유는 프로그램이 서로 다른 위치에서 중단되기 때문입니다. 실행 줄(oparray=0x21d58b0)이 표시되면 이는 PHP가 oparray를 실행하는 진입점입니다. gdb 아래에 f 6을 입력합니다(호출 스택 번호를 통해 사용 가능).

코드 복사 코드는 다음과 같습니다.

(gdb) f 6
#6 /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410에서 실행(op_array=0x21d58b0)의 0x000000000093b5fd
410 if ((ret = OPLINE->handler(execute_data TSRMLS_CC)) > 0) {
(gdb) p *op_array
$2 = {유형 = 2 ' refcount = 0x7f726d0870f0, opcodes = 0x7f726424d600, last = 8, vars = 0x7f726424e890, last_var = 2, T = 1, brk_cont_array = 0x0, last_brk_cont = 0, try_catch_array = 0x0,
last_try_catch = 0, static_variables = 0x0, this_var = 4294967295, 파일 이름 = 0x7f726424ba38 "/home/htf/wwwroot/include.php", line_start = 12, line_end = 15, doc_comment = 0x0,
doc_comment_len = 0, early_bound = 4294967295, 리터럴 = 0x7f726424eae0, last_literal = 4, run_time_cache = 0x7f726450bfb0, last_cache_slot = 1, 예약됨 = {0x0, 0x0, 0x0, 0x0}}

여기의 파일 이름은 op_array가 어떤 PHP 파일인지 표시합니다. 그런 다음 f 0을 입력하여 현재 위치를 입력합니다.

코드 복사 코드는 다음과 같습니다.
(gdb) p **executor_globals.opline_ptr
$4 = {핸들러 = 0x93ff9c , op1 = {상수 = 1680133296, var = 1680133296, num = 1680133296, 해시 = 140129283132592, opline_num = 1680133296,
jmp_addr = 0x7f726424ccb0, zv = 0x7f726424ccb0, 리터럴 = 0x7f726424ccb0, ptr = 0x7f726424ccb0}, op2 = {상수 = 0, var = 0, num = 0, 해시 = 0, opline_num = 0, jmp_addr = 0x 0,
zv = 0x0, 리터럴 = 0x0, ptr = 0x0}, 결과 = {상수 = 32, var = 32, num = 32, 해시 = 32, opline_num = 32, jmp_addr = 0x20, zv = 0x20, 리터럴 = 0x20, ptr = 0x20},
확장_값 = 1, lineno = 5, opcode = 60 '

여기서 lineno는 OPCODE가 위치한 코드의 줄 번호를 나타냅니다. 해당 파일로 이동하면 어떤 코드 줄인지 확인할 수 있습니다. 여기서는 하나씩 소개하지 않고, GDB를 이용하면 더 많은 정보를 볼 수 있습니다.

zbacktrace 사용법

zend는 명령을 캡슐화하고 PHP 함수의 호출 관계를 직접 볼 수 있는 gdb 스크립트를 공식적으로 제공합니다. PHP 소스 코드 패키지의 루트 디렉터리에 .gdbinit가 있습니다.

사용

코드 복사 코드는 다음과 같습니다.

소스 your_php_src_path/.gdbinit
zbacktrace

PHP 함수의 호출 스택을 직접 볼 수 있습니다.

위 내용은 이 글의 전체 내용입니다. 모두 마음에 드셨으면 좋겠습니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.