古詩詞大全網 - 成語故事 - 如何在 Oracle Linux 上使用 PHP DTrace

如何在 Oracle Linux 上使用 PHP DTrace

DTrace 是壹個受歡迎的、“總是可用”的跟蹤實用程序,用於識別開發和生產系統中的性能和行為問題。適用於 Oracle Linux 的標準“UEK3”內核包括了 DTrace 支持。通過跟蹤 PHP 核心和 PHP OCI8 擴展中的用戶探測器,我們可以有效地識別 PHP 腳本問題。DTrace 讓您可以跟蹤用戶應用程序與操作系統之間的交互。

在 Oracle Linux 上,DTrace 實用程序需要壹個 Oracle Unbreakable Linux Network (ULN) 訂閱。

安裝 Oracle Linux 和 Oracle Unbreakable Enterprise Kernel 版本 3

首先,通過 Oracle E-Delivery 安裝 Oracle Linux 6.4。

安裝 Oracle Linux 6.4 之後,確保在 ULN 中啟用了“Unbreakable Enterprise Kernel Release 3 for Oracle Linux 6 (x86_64) - Latest”頻道。安裝 UEK3 內核:

# yum install kernel-uek

在 ULN 中啟用“Oracle Linux 6 Dtrace Userspace Tools (x86_64) - Latest”頻道,安裝 DTrace 實用程序:

# yum install dtrace-utils

重新引導到新的 UEK3 3.8.13 內核。

安裝 PHP

需要使用 --enable-dtrace 參數構建 PHP。您可以安裝壹些啟用了 DTrace 的預先構建的評估版 RPM,請參見“使用 Oracle Linux“playground”預構建軟件包的 DTrace PHP”。

也可以按照如下所述重新構建 PHP:

從 php.net 下載 PHP 5.4.20 或 PHP 5.5.4 或更高版本,然後進行解壓縮:

$ tar -xJf php-5.4.20.tar.bz2

$ cd php-5.4.20

配置 PHP:

$ ./configure \

--prefix=$HOME/p54 \

--enable-dtrace \

--disable-all --disable-cgi \

--with-pear --enable-xml --enable-libxml --with-zlib

這將構建壹個啟用了 DTrace 的最小的命令行 PHP。所有不需要的擴展均被禁用。您可根據需要包含其他擴展。

--prefix 選項表示安裝到壹個本地目錄,方便您查看安裝的文件。用完快照之後,清理該目錄很容易。

pear、xml 和 zlib 選項允許使用 pecl 命令。

生成 PHP 二進制文件並進行安裝:

$ make

$ make install

將 php.ini-development 復制到 $HOME/p54/lib/php.ini,編輯該文件來設置時區,例如:

date.timezone = America/Los_Angeles

安裝適用於 Oracle Database 的 PHP OCI8

評估版 PHP RPM 包括可以連接到 Oracle Database 的 OCI8 擴展;請參見前面提到的博文。

不過,如果您自己編譯 PHP,需要手動將 PHP OCI8 作為壹個“***享的”擴展添加:

在 ULN 上啟用“Oracle Software for Oracle Linux 6 (x86_64)”頻道。

以 root 用戶身份安裝 Oracle Instant Client:

# yum install oracle-instantclient12.1-basic oracle-instantclient12.1-devel

作為普通用戶,設置 PHP 的 PATH:

$ export PATH=$HOME/p54/bin:$PATH

如果需要訪問 :80/

設置用來啟用 DTrace 的環境變量 PHP_DTRACE,然後安裝 PHP OCI8:

$ PHP_DTRACE=yes pecl install oci8-2.0.2

本文後面將要用到的 DTrace 探測器定義基於 PHP OCI8 2.0.2,因此需要安裝明確的版本。如果您要安裝更高的版本,請查看探測器及其參數,了解最新版本中的小改進。

當提示 ORACLE_HOME 目錄時,無需輸入文本,直接按 Return。安裝將自動檢測 Oracle Instant Client RPM。配置將繼續,輸出將包含類似下面的內容:

[ . . . ]

checking for Oracle Database OCI8 support... yes, shared

checking PHP version... 5.4.20, ok

checking OCI8 DTrace support... yes

[ . . . ]

configure: WARNING: OCI8 extension: ORACLE_HOME is not set,

looking for default Oracle Instant Client instead

checking Oracle Instant Client directory...

/usr/lib/oracle/12.1/client64/lib

checking Oracle Instant Client SDK header directory...

/usr/include/oracle/12.1/client64

checking Oracle Instant Client library version compatibility... 12.1

[ . . . ]

再次編輯 php.ini,添加 PHP OCI8:

extension=oci8.so

確認安裝:

$ php --ri oci8

oci8

OCI8 Support => enabled

OCI8 DTrace Support => enabled

OCI8 Version => 2.0.2-dev

Revision => $Id: b30fb4bef45d9f5ce8a56b736f1546ea0cff08ef $

Oracle Run-time Client Library Version => 12.1.0.1.0

Oracle Compile-time Instant Client Version => 12.1

Directive => Local Value => Master Value

oci8.max_persistent => -1 => -1

oci8.persistent_timeout => -1 => -1

oci8.ping_interval => 60 => 60

oci8.privileged_connect => Off => Off

oci8.statement_cache_size => 20 => 20

oci8.default_prefetch => 100 => 100

oci8.old_oci_close_semantics => Off => Off

oci8.connection_class => no value => no value

oci8.events => Off => Off

Statistics =>

Active Persistent Connections => 0

Active Connections => 0

PHP OCI8 安裝說明

為了支持 DTrace,需要從 PECL 安裝 PHP OCI8 2.0,因為 PHP 5.4 和 PHP 5.5 包含的 PHP OCI8 1.4 沒有 DTrace 探測器。將來,在 PHP 5.6 發布後,您就能夠在構建 PHP 時配置支持 DTrace 的 PHP OCI8。

當然,您可以通過 Oracle Instant Client zip 文件安裝 PHP OCI8,也可以僅使用現有的 ORACLE_HOME 安裝。

您可以在不包含或未配置 DTrace 的 PHP 版本上安裝啟用 DTrace 的 PHP OCI8。這包括 PHP 早期版本。您將能夠跟蹤 PHP OCI8 探測器,但無法跟蹤核心 PHP 探測器。類似地,您可以在啟用 DTrace 的 PHP 上安裝禁用 DTrace 的 PHP OCI8。

如果您使用 phpize 和 configure(而不是 pecl)從 PECL 安裝 PHP OCI8 2.0 ,則仍需設置 PHP_DTRACE=yes。這是因為 PECL 軟件包有限的 configure 腳本將忽略 --enable-dtrace 選項。

PHP OCI8 2.0 配置腳本適合“真正的”DTrace 使用,但 Linux SystemTap 不會跟蹤擴展。

註意,DTrace 優化後的二進制文件產生的輸出可能與我們所見到的代碼不盡相同。

驗證 PHP DTrace 探測器

以 root 用戶身份啟用 DTrace,允許普通用戶記錄跟蹤信息:

# modprobe fasttrap

# chmod 666 /dev/dtrace/helper

您可以使用 ACL 軟件包規則限制特定用戶對設備的訪問,而非使用 chmod 命令。

作為普通用戶運行 php,不帶任何選項。PHP 將啟動,然後等待輸入:

$ php

以 root 用戶身份列出可用的 DTrace 探測器。列出了 PHP 核心探測器和 PHP OCI8 探測器:

# dtrace -l -m php -m oci8.so

4 php9559 php dtrace_compile_file compile-file-entry

5 php9559 php dtrace_compile_file compile-file-return

6 php9559 php zend_error error

7 php9559 php ZEND_CATCH_SPEC_CONST_CV_HANDLER exception-caught

8 php9559 php zend_throw_exception_internal exception-thrown

9 php9559 php dtrace_execute_ex execute-entry

10 php9559 php dtrace_execute_internal execute-entry

11 php9559 php dtrace_execute_ex execute-return

12 php9559 php dtrace_execute_internal execute-return

13 php9559 php dtrace_execute_ex function-entry

14 php9559 php dtrace_execute_ex function-return

15 php9559 php php_request_shutdown request-shutdown

16 php9559 php php_request_startup request-startup

17 php9559 oci8.so php_oci_dtrace_check_connection oci8-check-connection

18 php9559 oci8.so php_oci_do_connect oci8-connect-entry

19 php9559 oci8.so php_oci_persistent_helper oci8-connect-expiry

20 php9559 oci8.so php_oci_do_connect_ex oci8-connect-lookup

21 php9559 oci8.so php_oci_pconnection_list_np_dtor oci8-connect-p-dtor-close

22 php9559 oci8.so php_oci_pconnection_list_np_dtor oci8-connect-p-dtor-release

23 php9559 oci8.so php_oci_do_connect oci8-connect-return

24 php9559 oci8.so php_oci_do_connect_ex oci8-connect-type

25 php9559 oci8.so php_oci_error oci8-error

26 php9559 oci8.so php_oci_statement_execute oci8-execute-mode

27 php9559 oci8.so php_oci_create_spool oci8-sesspool-create

28 php9559 oci8.so php_oci_create_session oci8-sesspool-stats

29 php9559 oci8.so php_oci_create_session oci8-sesspool-type

30 php9559 oci8.so php_oci_statement_create oci8-sqltext

PHP 手冊中詳細記載了核心 PHP 探測器。PHP OCI8 探測器如下所述。

在用戶終端,用 Ctrl-C 停止 php 可執行文件。

$ php

^C

$

PHP OCI8 2.0 DTrace 探測器概述

靜態 PHP OCI8 2.0 探測器可分為“用戶”探測器和“維護者”探測器。後者對於 PHP OCI8 維護者更有用,可在擴展開發期間驗證功能。所有探測器均以參數形式返回數據。官方文檔中詳細介紹了 PHP OCI8 DTrace 探測器。

在 OCI8 2.0.2 中,用戶探測器包括:

oci8-connect-entry — 由 oci_connect()、oci_pconnect() 和 oci_new_connect() 啟動。在建立數據庫連接之前觸發。

char *username — 連接用戶名。

char *dbname — 數據庫連接字符串。

char *charset — 指定的字符集。

long session_mode — OCI_SYSDBA (0x2)、OCI_SYSOPER (0x4) 和 OCI_CRED_EXT (1<<31) 的二進制“或”(或者過去我所使用平臺上的 -2147483648)。默認情況下,設置為 0。

int persistent — 如果調用了 oci_pconnect(),則設置為 1;否則設置為 0。

int exclusive — 如果調用了 oci_new_connect(),則設置為 1;否則設置為 0。

oci8-connect-return — 在連接的末尾觸發。

void *connection — 連接結構的地址。

oci8-check-connection — 如果 Oracle 錯誤可能已經導致連接失效,則啟動。

void *connection — 連接結構的地址。

int is_open — 如果 errcode 或 server_status 指示連接無效且必須重新創建,則為 0。

long errcode — Oracle 錯誤編號。

unsigned long server_status — 為 Oracle 庫的壹個指示器,指示連接是否無效。如果因為 errcode 指示連接無效導致 is_open 為 0,則 server_status 為其默認值 1。

oci8-sqltext — 執行 oci_parse() 時啟動。

void *connection — 連接結構的地址。

char *sql — 所執行的 SQL 語句的文本。

oci8-error — 如果發生 Oracle 錯誤,則啟動。

int status — Oracle 返回失敗的 Oracle 庫調用的狀態,如 -1 代表 Oracle 的 OCI_ERROR,1 代表 Oracle 的 OCI_SUCCESS_WITH_INFO。請參見 Oracle 的 oci.h,了解所有定義。

long errcode — Oracle 錯誤編號。

oci8-execute-mode — 指示 oci_execute() 調用的提交狀態。

void *connection — 連接結構的地址。

unsigned int mode — 傳遞給 Oracle 庫的模式,如 OCI_NO_AUTO_COMMIT (0x00)、OCI_DESCRIBE_ONLY (0x10) 或 OCI_COMMIT_ON_SUCCESS (0x20)。

註:最新的 OCI8 2.0.6 中包含 oci8-connection-close 探測器,有幾個探測器現在也具有 client_id 參數和指向語句結構的指針。

維護者探測器如下所示。參數描述請參考 PHP OCI8 源代碼。

oci8-connect-p-dtor-close

void *connection

oci8-connect-p-dtor-release

void *connection

oci8-connect-lookup

void *connection

int is_stub

oci8-connect-expiry

void *connection

int is_stub

long idle_expiry

long timestamp

oci8-connect-type

int persistent

int exclusive

void *connection

long num_persistent

long num_connections

oci8-sesspool-create

void *session_pool

oci8-sesspool-stats

unsigned long free

unsigned long busy

unsigned long open

oci8-sesspool-type

int type

void *session_pool

PHP OCI8 2.0 中的探測器代替了 PHP OCI8 1.4 中使用的 oci_internal_debug() 跟蹤。該函數已變成壹條空指令,不執行任何操作。

使用 PHP OCI8 和 DTrace

執行以下步驟。

創建壹個簡單的 PHP 文件 oci8.php 來查詢數據庫:

<?php

error_reporting(0);

ini_set('display_errors', 'Off');

function do_query($c, $sql)

{

$s = oci_parse($c, $sql);

if (!$s)

return;

$r = oci_execute($s);

if (!$r)

return;

while (($row = oci_fetch_row($s)) != false) {

foreach ($row as $item) {

echo $item . " ";

}

echo "\n";

}

}

$c = oci_new_connect('hr', 'welcome', 'localhost/pdborcl');

do_query($c, "select city from locations where rownum < 5 order by 1");

do_query($c, "select something from does_not_exist");

?>

創建壹個 D 腳本 user_oci8.d 來探測 oci8.php 的執行:

#!/usr/sbin/dtrace -Zs

# This script is for OCI8 2.0.2

php*:::oci8-connect-entry

{

printf("PHP connect-entry\n");

printf("\t username %s\n", arg0 ? copyinstr(arg0) : "");

printf("\t dbname %s\n", arg1 ? copyinstr(arg1) : "");

printf("\t charset %s\n", arg2 ? copyinstr(arg2) : "");

printf("\t session_mode %ld\n", (long)arg3);

printf("\t persistent %d\n", (int)arg4);

printf("\t exclusive %d\n", (int)arg5);

}

php*:::oci8-connect-return

{

printf("PHP oci8-connect-return\n");

printf("\t connection 0x%p\n", (void *)arg0);

}

php*:::oci8-connection-close

{

printf("PHP oci8-connect-close\n");

printf("\t connection 0x%p\n", (void *)arg0);

}

php*:::oci8-error

{

printf("PHP oci8-error\n");

printf("\t status %d\n", (int)arg0);

printf("\t errcode %ld\n", (long)arg1);

}

php*:::oci8-check-connection

{

printf("PHP oci8-check-connection\n");

printf("\t connection 0x%p\n", (void *)arg0);

printf("\t is_open %d\n", arg1);

printf("\t errcode %ld\n", (long)arg2);

printf("\t server_status %lu\n", (unsigned long)arg3);

}

php*:::oci8-sqltext

{

printf("PHP oci8-sqltext\n");

printf("\t connection 0x%p\n", (void *)arg0);

printf("\t sql %s\n", arg0 ? copyinstr(arg1) : "");

}

php*:::oci8-execute-mode

{

printf("PHP oci8-execute-mode\n");

printf("\t connection 0x%p\n", (void *)arg0);

printf("\t mode 0x%x\n", arg1);

}

使用 OCI8 2.0.6,壹些參數的數值需要調整,才能滿足現在壹些新增參數的需要。

以 root 用戶身份啟動 D 腳本。該腳本將暫停,等待探測器觸發:

# chmod +x user_oci8.d

# ./user_oci8.d

(稍後,體驗完 PHP 之後,可以使用 Ctrl-C 關閉此終端。)

在另壹個窗口中運行命令行 PHP。顯示成功查詢的輸入:

$ php oci8.php

Beijing

Bern

Bombay

Geneva

在運行 D 腳本的 root 終端,將顯示 PHP 執行期間觸發的探測器:

# ./user_oci8.d

dtrace: script 'user_oci8.d' matched 0 probes

CPU ID FUNCTION:NAME

1 18 php_oci_do_connect:oci8-connect-entry PHP connect-entry

username hr

dbname localhost/pdborcl

charset

session_mode 0

persistent 0

exclusive 0

0 23 php_oci_do_connect:oci8-connect-return PHP oci8-connect-return

connection 0x7f64e112cff0

0 31 php_oci_statement_create:oci8-sqltext PHP oci8-sqltext

connection 0x7f64e112cff0

sql select city from locations where rownum < 5 order by 1

0 27 php_oci_statement_execute:oci8-execute-mode PHP oci8-execute-mode

connection 0x7f64e112cff0

mode 0x20

0 31 php_oci_statement_create:oci8-sqltext PHP oci8-sqltext

connection 0x7f64e112cff0

sql select something from does_not_exist

0 27 php_oci_statement_execute:oci8-execute-mode PHP oci8-execute-mode

connection 0x7f64e112cff0

mode 0x20

0 26 php_oci_error:oci8-error PHP oci8-error

status -1

errcode 942

0 17 php_oci_dtrace_check_connection:oci8-check-connection PHP oci8-check-connection

connection 0x7f64e112cff0

is_open 1

errcode 942

server_status 1

0 25 php_oci_connection_close:oci8-connection-close PHP oci8-connect-close

connection 0x7f64e112cff0

(將 -q 添加到 user_oci8.d 的 /usr/sbin/dtrace 參數將隱藏 CPU 和 ID 詳細信息。)

在多 CPU 計算機上,探測器可能不會順序出現,具體取決於哪個 CPU 正在處理探測器。顯示探測器時間戳將有助於消除混淆,例如:

php*:::oci8-connect-entry

{

printf("PHP connect-entry at %lld\n", walltimestamp);

}

從 user_oci8.d DTrace 輸出中,您可以看到以下內容:

發起的連接 (oci8-connect-entry)。連接到 localhost/pdborcl 數據庫的用戶 hr。因為 exclusive 和 persistent 均為 0,所以是壹個 oci_connect() 調用。未請求任何顯式的字符集。請求的是默認的會話模式(oci_connect 的第五個可選參數)。

對兩條 SQL 語句進行分析 (oci8-sqltext),並以 0x20(即 OCI_COMMIT_ON_SUCCESS)模式執行 (oci-execute-mode)。

生成了 (oci8-error) 壹條 Oracle 錯誤 — ORA-942(表或視圖不存在)。

該錯誤導致要驗證連接狀態 (oci8-check-connection)。is_open 的值為 1,表示連接正常。

通過這些信息,您可以跟蹤有問題的語句執行和連接問題。

總結

DTrace 是壹個非常強大的實用程序,本文只是有關其用法的只言片語。通過上述示例,可將 PHP OCI8 跟蹤與核心 PHP 跟蹤相集成。您可以設置 PHP 函數調用的時間,計算調用的次數。您可以深入了解 PHP 進行的操作系統調用。Bryan Cantrill 在 DTrace 和 PHP(演示)中發布了壹些核心 PHP 跟蹤的示例。(註意,博客平臺升級導致其博文中的壹個反斜杠顯示為兩個反斜杠。而且,您不再需要單獨的 PHP DTrace 擴展。)Brendan Gregg 的 DTrace Toolkit 包含許多有用的 DTrace 腳本。還有各種不同的博客。

記住,DTrace 的目的是始終啟用其功能,適合開發和生產(此過程最需要 DTrace)。DTrace 的設計意味著探測器不受監視時零開銷。