使用GDB调试PHP代码,解决PHP代码死循环问题_PHP
最近在帮同事解决Swoole Server问题时,发现有1个worker进程一直处于R的状态,而且CPU耗时非常高。初步断定是PHP代码中发生死循环。
下面通过一段代码展示如何解决PHP死循环问题。
代码如下:
#dead_loop.php
$array = array();
for($i = 0; $i
{
$array[] = $i;
}
include __DIR__."/include.php";
#include.php
while(1)
{
usleep(10);
$keys = array_flip($array);
$index = array_search(rand(1500, 9999), $array);
$str = str_repeat('A', $index);
$strb = test($index, $str);
}
function test($index, $str)
{
return str_replace('A', 'B', $str);
}
通过ps aux得到进程ID和状态如下,使用gdb -p 进程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
#0 0x00000000008cc03f in zend_mm_check_ptr (heap=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)
at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:1485
#1 0x00000000008cd643 in _zend_mm_free_int (heap=0x1eaa2c0, p=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) at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2064
#2 0x00000000008cebf7 in _efree (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) at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2436
#3 0x00000000008eda0a in _zval_ptr_dtor (zval_ptr=0x25849a0, __zend_filename=0xee3d40 "/home/htf/workspace/php-5.4.27/Zend/zend_variables.c", __zend_lineno=182)
at /home/htf/workspace/php-5.4.27/Zend/zend_execute_API.c:437
#4 0x00000000008fe687 in _zval_ptr_dtor_wrapper (zval_ptr=0x25849a0) at /home/htf/workspace/php-5.4.27/Zend/zend_variables.c:182
#5 0x000000000091259f in zend_hash_destroy (ht=0x7f7263f6e380) at /home/htf/workspace/php-5.4.27/Zend/zend_hash.c:560
#6 0x00000000008fe2c5 in _zval_dtor_func (zvalue=0x7f726426fe50, __zend_filename=0xeea290 "/home/htf/workspace/php-5.4.27/Zend/zend_execute.c", __zend_lineno=901)
at /home/htf/workspace/php-5.4.27/Zend/zend_variables.c:45
#7 0x0000000000936656 in _zval_dtor (zvalue=0x7f726426fe50, __zend_filename=0xeea290 "/home/htf/workspace/php-5.4.27/Zend/zend_execute.c", __zend_lineno=901)
at /home/htf/workspace/php-5.4.27/Zend/zend_variables.h:35
#8 0x0000000000939747 in zend_assign_to_variable (variable_ptr_ptr=0x7f7263f8e738, value=0x7f726426f6a8) at /home/htf/workspace/php-5.4.27/Zend/zend_execute.c:901
#9 0x0000000000997ee5 in ZEND_ASSIGN_SPEC_CV_VAR_HANDLER (execute_data=0x7f726d04b2a8) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:33168
#10 0x000000000093b5fd in execute (op_array=0x21d58b0) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410
#11 0x0000000000901692 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/htf/workspace/php-5.4.27/Zend/zend.c:1315
#12 0x000000000087926a in php_execute_script (primary_file=0x7ffffe0038d0) at /home/htf/workspace/php-5.4.27/main/main.c:2502
#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 in main (argc=2, argv=0x7ffffe004d18) at /home/htf/workspace/php-5.4.27/sapi/cli/php_cli.c:1365
执行gdb后,死循环的进程会变成T的状态,表示正在Trace。这个是独占的,所以不能再使用strace/gdb或者其他ptrace工具对此进程进行调试。另外此进程会中断执行。gdb输入c后,程序继续向下运行。然后再次按下ctrl + c中断程序。 通过bt命令查看进程的调用栈。
代码如下:
(gdb) bt
#0 _zend_mm_alloc_int (heap=0x1eaa2c0, size=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) at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:1895
#1 0x00000000008ceb86 in _emalloc (size=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) at /home/htf/workspace/php-5.4.27/Zend/zend_alloc.c:2425
#2 0x0000000000911d85 in _zend_hash_index_update_or_next_insert (ht=0x2257a10, h=3972, pData=0x7ffffe0012b0, nDataSize=8, pDest=0x0, flag=1,
__zend_filename=0xe43410 "/home/htf/workspace/php-5.4.27/ext/standard/array.c", __zend_lineno=2719) at /home/htf/workspace/php-5.4.27/Zend/zend_hash.c:412
#3 0x00000000007767e1 in zif_array_flip (ht=1, return_value=0x7f726424ea68, return_value_ptr=0x0, this_ptr=0x0, return_value_used=1)
at /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
#5 0x00000000009400e6 in ZEND_DO_FCALL_SPEC_CONST_HANDLER (execute_data=0x7f726d04b2a8) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:2233
#6 0x000000000093b5fd in execute (op_array=0x21d58b0) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410
两次的BT信息不一样,这是因为程序在不同的位置中断。看到execute (oparray=0x21d58b0) 这一行,这里就是PHP执行oparray的入口了。gdb下输入f 6,(通过调用栈编号可得)。
代码如下:
(gdb) f 6
#6 0x000000000093b5fd in execute (op_array=0x21d58b0) at /home/htf/workspace/php-5.4.27/Zend/zend_vm_execute.h:410
410 if ((ret = OPLINE->handler(execute_data TSRMLS_CC)) > 0) {
(gdb) p *op_array
$2 = {type = 2 '\002', function_name = 0x7f726d086540 "test", scope = 0x0, fn_flags = 134217728, prototype = 0x0, num_args = 2, required_num_args = 2, arg_info = 0x7f726d086bd8,
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, filename = 0x7f726424ba38 "/home/htf/wwwroot/include.php", line_start = 12, line_end = 15, doc_comment = 0x0,
doc_comment_len = 0, early_binding = 4294967295, literals = 0x7f726424eae0, last_literal = 4, run_time_cache = 0x7f726450bfb0, last_cache_slot = 1, reserved = {0x0, 0x0, 0x0, 0x0}}
这里的filename就能看到op_array是哪个PHP文件的。然后输入f 0进入当前位置。
代码如下:
(gdb) p **executor_globals.opline_ptr
$4 = {handler = 0x93ff9c , op1 = {constant = 1680133296, var = 1680133296, num = 1680133296, hash = 140129283132592, opline_num = 1680133296,
jmp_addr = 0x7f726424ccb0, zv = 0x7f726424ccb0, literal = 0x7f726424ccb0, ptr = 0x7f726424ccb0}, op2 = {constant = 0, var = 0, num = 0, hash = 0, opline_num = 0, jmp_addr = 0x0,
zv = 0x0, literal = 0x0, ptr = 0x0}, result = {constant = 32, var = 32, num = 32, hash = 32, opline_num = 32, jmp_addr = 0x20, zv = 0x20, literal = 0x20, ptr = 0x20},
extended_value = 1, lineno = 5, opcode = 60 '
这里的lineno表示OPCODE所在的代码行数,可以到对应文件里去看下是哪行代码。使用GDB可以查看到更多的信息,这里就不再一一介绍了,有兴趣各位可以自行尝试。
zbacktrace的使用
zend官方提供了一个gdb的脚本,对指令进行了封装,可以直接看到php函数的调用关系。在php源代码包的根目录中有一个.gdbinit。使用
代码如下:
source your_php_src_path/.gdbinit
zbacktrace
可以直接看到PHP函数的调用堆栈。
以上就是本文的全部内容了,希望大家能够喜欢。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック

GDB を使用して Linux カーネルをデバッグするための一般的な構成テクニック はじめに: Linux 開発では、GDB を使用してカーネルをデバッグすることは非常に重要なスキルです。 GDB は、開発者がカーネル内のバグを迅速に見つけて解決するのに役立つ強力なデバッグ ツールです。この記事では、いくつかの一般的な GDB 構成テクニックと、GDB を使用して Linux カーネルをデバッグする方法を紹介します。 1. GDB 環境の構成 まず、Linux システム上で GDB 環境を構成する必要があります。システムに GDB ツールがインストールされていることを確認してください

GDB を使用して Linux で組み込み ARM プログラムをデバッグするための一般的な構成方法 特殊なコンピューター システムとして、組み込みシステムは通常、電子デバイスに統合され、ハードウェア リソースの制御と管理に使用されます。組み込みシステムの動作をデバッグおよび分析するには、専用のツールを使用する必要があります。その中でも、GDB は、組み込みシステム上で実行し、プログラムと通信できる、一般的に使用されるオープン ソース デバッガーです。この記事では、GDB を使用して Linux で組み込み ARM プログラムをデバッグするための一般的な構成方法を紹介し、コード例を示します。

GDB を使用して Go 関数をデバッグする手順は次のとおりです。 GDB と Go デバッグ パッケージをインストールします。デバッグ情報を有効にしてプログラムをコンパイルします。 GDB を使用してプログラムを起動します。ブレークポイントを設定します。 run コマンドを使用してプログラムを開始します。デバッグにはデバッグ コマンドを使用します。

C++ 開発における無限ループ問題の解決方法 C++ 開発において、無限ループは非常に一般的ですが、非常に難しい問題です。プログラムが無限ループに陥ると、プログラムが正常に実行できなくなり、場合によってはシステムがクラッシュする可能性があります。したがって、無限ループの問題を解決することは、C++ 開発において不可欠なスキルの 1 つです。この記事では、無限ループの問題を解決するための一般的な方法をいくつか紹介します。ループ条件の確認 無限ループの最も一般的な原因の 1 つは、ループ条件が正しくないことです。ループ条件が常に true の場合、ループは引き続き実行され、無限ループが発生します。

Linux で GDB を使用してマルチスレッド プログラムをデバッグするための一般的な構成方法 はじめに: マルチスレッド プログラミングでは、デバッグは不可欠なタスクです。 GDB は、マルチスレッド プログラムのエラーを見つけて解決するのに役立つ強力なデバッガです。この記事では、GDB を使用して Linux でマルチスレッド プログラムをデバッグするための一般的な構成方法を紹介し、コード例を示して、読者が GDB をよりよく理解して使用できるようにしたいと考えています。 1. GDB のインストール まず、Linux システムに GDB をインストールする必要があります。ターミナルに入力します

Linux システムでは、デバッグはプログラムの開発および保守プロセスの重要な部分です。開発者によるより効果的なデバッグを支援するために、Linux はさまざまな強力なデバッグ ツールとテクノロジを提供します。この記事では、開発者がより適切にデバッグできるようにするために、一般的に使用される Linux デバッグ ツールとテクニックをいくつか簡単に紹介します。 1. デバッグ ツール 1.gdb Linux システムでは、gdb はデバッグ ツールの 1 つとして広く使用されており、開発者がプログラム クラッシュの原因を追跡するのに役立ち、プログラムのステータスを確認し、変数を変更し、コードを実行する一連のコマンドを提供します。 。デバッグを開始するには、次のコマンドを使用できます。 $gdb./program ここで、「./program」はデバッグする実行可能ファイルを表します。 gdbが起動したら、

解決方法: Java アルゴリズム エラー: 無限ループ はじめに: Java プログラムを作成する過程で、さまざまなエラーや例外が頻繁に発生します。その中でもよくある問題が、プログラムが無限ループ状態に陥り、プログラムが正常に実行できなくなる無限ループです。この記事では、Java アルゴリズムの無限ループ問題を解決する方法を説明し、いくつかのサンプル コードを提供します。 1. 無限ループの定義と原因: 無限ループとは、正常終了条件が存在しないプログラム内のループ構造を指し、そのループ内でプログラムが無限ループで実行されます。

Go 言語は、効率的な同時実行パフォーマンスと組み込みのメモリ管理機能により、開発者の間でますます好まれているオープンソース プログラミング言語です。しかし、Go 言語の開発では、開発者に多くの頭痛の種を与える無限ループの問題に遭遇することがあります。この記事では、Go 言語開発で遭遇する無限ループの問題について説明し、いくつかの解決策を提供します。 1. 無限ループとは何ですか?無限ループとは、プログラム内のコードのセクションが無限回繰り返し実行され、プログラムが実行を継続できなくなる状況を指します。通常、無限ループはコード ロジック エラーまたはコードの欠如によって発生します。
