在 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 的設計意味著探測器不受監視時零開銷。