CPU が 100% に近い場合、私の考えは、まず CPU サージの原因を見つけることです。問題がある可能性があるコード スニペットを特定するためにプロセスが実行しているコード。次に、問題のあるコード部分を注意深く分析して原因を特定します。
プログラムが c または c++ で書かれている場合、実行されているコード行を簡単に見つけることができます。ただし、プログラムは PHP で記述されています。問題がある可能性のあるコード行をどのように見つければよいでしょうか? この問題は、この記事で解決されます。
背景知識:
phpがインタプリタ言語であることは誰もが知っています。ユーザーが記述した PHP コードはオペコードを生成し、インタープリタ エンジンによって解釈されて実行されます。解釈実行プロセス中に、実行プロセス中に使用されるさまざまなデータを含むグローバル変数が存在します。それはexecutor_globalsです。彼の型定義は、ソース コードの Zend/zend_globals.h ファイルにあります。
コードは次のとおりです | |
struct _zend_executor_globals { zval uninitialized_zval; zval error_zval; zend_ptr_stack arg_types_stack; /* シンボル テーブル キャッシュ */ zend_op **opline_ptr; ハッシュテーブル *active_symbol_table; HashTable Included_files; /* ファイルは既に組み込まれています */ JMP_BUF *救済; int error_reporting; zend_op_array *active_op_array; ハッシュテーブル *関数テーブル; /* 関数記号テーブル */ zend_class_entry *スコープ; zval *これ; 長い精度; intticks_count; zend_bool in_execution; /* 拡張情報サポート用 */ #ifdef ZEND_WIN32 ハッシュテーブルの通常リスト; zend_vm_stack argument_stack; int user_error_handler_error_reporting; zend_error_handling_t error_handling; /* タイムアウトのサポート */ int lambda_count; ハッシュテーブル *ini_directives; zend_objects_store オブジェクトストア; struct _zend_execute_data *current_execute_data; struct _zend_module_entry *current_module; zend_property_info std_property_info; zend_bool アクティブ; void *saved_fpu_cw; 無効 *予約済み[ZEND_MAX_RESERVED_RESOURCES]; |
ここでは、私たちにとってより重要な 2 つの変数、active_op_array と current_execute_data についてのみ説明します。
active_op_array 変数には、エンジンが実行している op_array が格納されます (op_array が何であるかを知りたい場合は、クリックして表示してください)。 op_array のデータ型の定義は Zend/zend_compile.h にあります。
コードは次のとおりです | |
struct _zend_op_array { zend_bool doned_pass_two; zend_uint *refcount; zend_op *オペコード; zend_compiled_variable *vars; zend_uint T; zend_brk_cont_element *brk_cont_array; zend_try_catch_element *try_catch_array; /* 静的変数のサポート */ zend_op *start_op; zend_uint this_var; char *ファイル名; 無効 *予約済み[ZEND_MAX_RESERVED_RESOURCES]; |
定義を読んだ後は、これ以上言う必要はありません。定義中の filename と function_name にはそれぞれ実行中のファイル名とメソッド名が保存されます。
Current_execute_dataは、実行中のop_arrayのexecute_dataを保存します。 execute_data は、各 op_array の実行中に一部のデータを保存します。 Zend/zend_compile.h で定義されています:
コードは次のとおりです | |
struct _zend_execute_data { struct _zend_op *opline; zend_function_state function_state; zend_function *fbc; /* 呼び出される関数 */ zend_class_entry *called_scope; zend_op_array *op_array; zval *オブジェクト; Union _temp_variable *Ts; zval ***CV; ハッシュテーブル *symbol_table; struct _zend_execute_data *prev_execute_data; zval *old_error_reporting; zend_bool ネストされた; zval **original_return_value; zend_class_entry *current_scope; zend_class_entry *current_called_scope; zval *current_this; zval *current_object; struct _zend_op *call_opline; }; |
定義内のoplineは実行されるオペコードです。オペコードの構造は次のように定義されます:
コードは次のとおりです | |
struct _zend_op { opcode_handler_t ハンドラー; znode の結果; znode op1; znode op2; ulong 拡張値; uint lineno; zend_uchar オペコード; }; |
lineno はオペコードに対応する行番号です。
説明例:
上記のデータ構造定義を読んだ後、実行される PHP のファイル名、メソッド名、行番号を確認する方法はもうわかりましたか? まだ疑問がある場合は、以下の例を見てください。次のコードを含むファイル test.php を作成します:
コードは次のとおりです | |
関数 test1(){ while(true){ 睡眠(1); } } テスト1(); ?> |
phpスクリプトをcliモードで実行すると、実行時に追加されるプロセス番号は14973です。 gdb コマンドを使用してプロセスをデバッグします。
コードは次のとおりです | |
$sudo gdb -p 14973 (gdb) print (char *)executor_globals.active_op_array->ファイル名 $1 = 0x9853a34 "/home/xinhailong/test/php/test.php" (gdb) print (char *)executor_globals.active_op_array->function_name $2 = 0x9854db8 "テスト1" (gdb) print executor_globals->current_execute_data->opline->lineno 3 ドル = 4 |
明らかに、4行目のsleepメソッドを実行しています。
上記の方法が面倒な場合は、.gdbinit ファイルを使用できます。このファイルは、php ソース コードのルート ディレクトリにあります。使用方法:
コードは次のとおりです | |
$sudo gdb -p 14973 (gdb) ソース /home/xinhailong/.gdbinit (gdb) zbacktrace [0xa453f34] sleep(1) /home/xinhailong/test/php/test.php:4 [0xa453ed0] test1() /home/xinhailong/test/php/test.php:7 (gdb) |
余談:
php5.6 以降、phpdbg ツールが php に統合されています。 gdb が C 言語プログラムをデバッグするのと同じように、PHP プログラムをデバッグできます。ご興味がございましたら、以下のリンクを開いてご覧ください