?
Dieses Dokument verwendet PHP-Handbuch für chinesische Websites Freigeben
PostgreSQL允許對(duì)數(shù)據(jù)庫(kù)服務(wù)器進(jìn)行動(dòng)態(tài)跟蹤。這樣就允許在代碼內(nèi)特 定的點(diǎn)上調(diào)用外部工具來(lái)跟蹤執(zhí)行過(guò)程。目前此功能主要目的是為了提供給數(shù)據(jù)庫(kù)開(kāi)發(fā)者使用,它要求使用者對(duì)代碼非常熟悉。
許多跟蹤點(diǎn)(也被稱(chēng)為"探頭")已經(jīng)插入在源代碼中了,這些探針的目的是被用于 數(shù)據(jù)庫(kù)開(kāi)發(fā)者管理員,默認(rèn)情況下,探頭不編譯成 PostgreSQL; 用戶(hù)必須在編譯前運(yùn)行configure腳本時(shí)明確啟用它們。
目前,只有 DTrace 支持實(shí)用工具,它可 在OpenSolaris,Solaris10操作系統(tǒng),和MacOSXLeopard的。據(jù)預(yù)計(jì), DTrace將可以在FreeBSD上的未來(lái)和其他可能的 操作系統(tǒng)?!?SystemTap項(xiàng)目 Linux還提供了一個(gè)DTrace的等效。支持其他動(dòng)態(tài), 通過(guò)改變src/include/utils/probes.h中的定義為跟蹤實(shí)用程序在理論上是可能的 。
跟蹤點(diǎn)是默認(rèn)禁止的,你必須在運(yùn)行configure腳本在PostgreSQL時(shí)明確使 用--enable-dtrace選項(xiàng)來(lái)啟用DTrace支持。參見(jiàn)Section 15.5獲取更多信息。
Table 27-3顯示的是在源代碼中提 供的標(biāo)準(zhǔn)跟蹤點(diǎn),此外,也可以根據(jù)特定的具體問(wèn)題添加其它跟蹤點(diǎn)。 更多可以肯定會(huì)增加,以提高PostgreSQL的 觀測(cè)性。
Table 27-3. 內(nèi)置跟蹤點(diǎn)
名字 | 參數(shù) | 概述 |
---|---|---|
transaction-start | (LocalTransactionId) | 開(kāi)始新事務(wù),arg0是事務(wù)ID。 |
transaction-commit | (LocalTransactionId) | 事務(wù)成功完成,arg0是事務(wù)ID。 |
transaction-abort | (LocalTransactionId) | 當(dāng)交易完成失敗將觸發(fā)的探測(cè)器。 arg0是事務(wù)ID。 |
query-start | (constchar*) | 開(kāi)始處理查詢(xún)時(shí)將觸發(fā)的探測(cè)器。 arg0是查詢(xún)字符串。 |
query-done | (constchar*) | 查詢(xún)處理完成時(shí)將觸發(fā)的探測(cè)器。 arg0是查詢(xún)字符串。 |
query-parse-start | (constchar*) | 解析查詢(xún)開(kāi)始時(shí)將觸發(fā)的探測(cè)器。 arg0是查詢(xún)字符串。 |
query-parse-done | (constchar*) | 完整解析查詢(xún)時(shí)觸發(fā)的探測(cè)器。 arg0是查詢(xún)字符串。 |
query-rewrite-start | (constchar*) | 啟動(dòng)觸發(fā)的探測(cè)器時(shí),查詢(xún)重寫(xiě)。 arg0是查詢(xún)字符串。 |
query-rewrite-done | (constchar*) | 探頭觸發(fā)時(shí),查詢(xún)重寫(xiě)是完整的。 arg0是查詢(xún)字符串。 |
query-plan-start | () | 查詢(xún)規(guī)劃開(kāi)始時(shí)將觸發(fā)的探測(cè)器 |
query-plan-done | () | 查詢(xún)規(guī)劃完成時(shí)將觸發(fā)的探測(cè)器 |
query-execute-start | () | 執(zhí)行規(guī)劃開(kāi)始時(shí)將觸發(fā)的探測(cè)器 |
query-execute-done | () | 執(zhí)行規(guī)劃完成時(shí)將觸發(fā)的探測(cè)器 |
statement-status | (constchar*) | 服務(wù)進(jìn)程隨時(shí)更新pg_stat_activity.current_query 狀態(tài)時(shí)觸發(fā)的探測(cè)器。arg0是一個(gè)新的狀態(tài)字符串。 |
checkpoint-start | (int) | 檢查點(diǎn)開(kāi)始時(shí)觸發(fā)的探測(cè)器。arg0可以逐位標(biāo)記以區(qū)分不同的檢查點(diǎn)類(lèi)型, 如;shutdown,immediate,或force。 |
checkpoint-done | (int,int,int,int,int) | 檢查點(diǎn)完成時(shí)觸發(fā)的探測(cè)器。 (觸發(fā)列表列出檢查點(diǎn)處理過(guò)程中個(gè),序列中的下一個(gè)探測(cè)器) arg0表示要寫(xiě)入的緩沖區(qū)的數(shù)目。arg1表示總的緩沖區(qū)的數(shù)目。 arg2,arg3和arg4包含了增加,刪除和循環(huán)回收的xlog文件的數(shù)目。 |
clog-checkpoint-start | (bool) | 一個(gè)檢查點(diǎn)的CLOG部分開(kāi)始時(shí)觸發(fā)的探測(cè)器。 arg0對(duì)正常檢查點(diǎn)表示真,對(duì)關(guān)閉檢查點(diǎn)表示假。 |
clog-checkpoint-done | (bool) | 當(dāng)一個(gè)檢查點(diǎn)的CLOG部分完成時(shí)觸發(fā)的探測(cè)器。arg0的含義與CLOG-checkpoint-start一樣。 |
subtrans-checkpoint-start | (bool) | 當(dāng)一個(gè)檢查點(diǎn)的SUBTRANS部分開(kāi)始時(shí)觸發(fā)的探測(cè)器。arg0對(duì)正常檢查點(diǎn)表示真,對(duì) 關(guān)閉檢查點(diǎn)表示假。 |
subtrans-checkpoint-done | (bool) | 當(dāng)一個(gè)檢查點(diǎn)的SUBTRANS部分完成時(shí)觸發(fā)的探測(cè)器。arg0的含義與SUBTRANS-checkpoint-start一樣。 |
multixact-checkpoint-start | (bool) | 當(dāng)一個(gè)檢查點(diǎn)的MultiXact部分開(kāi)始時(shí)觸發(fā)的探測(cè)器。arg0對(duì)正常檢查點(diǎn)表示真,對(duì) 關(guān)閉檢查點(diǎn)表示假。 |
multixact-checkpoint-done | (bool) | 當(dāng)一個(gè)檢查點(diǎn)的MultiXact部分完成時(shí)觸發(fā)的探測(cè)器。arg0的含義與multixact-checkpoint-start一樣。 |
buffer-checkpoint-start | (int) | 開(kāi)始一個(gè)檢查點(diǎn)的緩沖區(qū)寫(xiě)部分時(shí)觸發(fā)的探測(cè)器。 arg0持有逐位標(biāo)識(shí)以區(qū)分不同的檢查點(diǎn)類(lèi)型,如shutdown,immediate或force。 |
buffer-sync-start | (int,int) | 檢查點(diǎn)期間,開(kāi)始寫(xiě)臟緩沖區(qū)時(shí)觸發(fā)的探測(cè)器(在識(shí)別出那個(gè)緩沖區(qū)必須寫(xiě)之后)。 arg0表示總緩沖區(qū)數(shù),arg1表示當(dāng)前臟的,需要寫(xiě)的緩沖區(qū)數(shù)。 |
buffer-sync-written | (int) | 在檢查點(diǎn)期間,每個(gè)緩沖區(qū)都被寫(xiě)了之后觸發(fā)的探測(cè)器, arg0表示緩沖區(qū)的ID號(hào)。 |
buffer-sync-done | (int,int,int) | 當(dāng)所有臟緩沖被寫(xiě)之后觸發(fā)的探測(cè)器。 arg0表示總緩沖區(qū)的數(shù)目。 arg1表示檢查點(diǎn)進(jìn)程實(shí)際寫(xiě)的緩沖區(qū)數(shù)。 arg2表示期望寫(xiě)的數(shù)目(arg1 of buffer-sync-start); 任何的不同會(huì)導(dǎo)致另一個(gè)進(jìn)程在檢查點(diǎn)發(fā)生時(shí)刷寫(xiě)緩沖區(qū)。 |
buffer-checkpoint-sync-start | () | 當(dāng)完成將臟緩沖區(qū)寫(xiě)入到內(nèi)核,并且還沒(méi)有發(fā)出fsync請(qǐng)求之前觸發(fā)的探測(cè)器。 |
buffer-checkpoint-done | () | 當(dāng)同步緩沖區(qū)到磁盤(pán)完成時(shí)觸發(fā)的探測(cè)器。 |
twophase-checkpoint-start | () | 當(dāng)一個(gè)檢查點(diǎn)的兩相階段狀態(tài)部分開(kāi)始時(shí)觸發(fā)的探測(cè)器。 |
twophase-checkpoint-done | () | 當(dāng)一個(gè)檢查點(diǎn)的兩相階段狀態(tài)部分完成時(shí)觸發(fā)的探測(cè)器。 |
buffer-read-start | (ForkNumber,BlockNumber,O id,O id,O id,bool,bool) | 當(dāng)開(kāi)始一次緩沖區(qū)讀時(shí)觸發(fā)的探測(cè)器。 arg0和arg1包含page塊中鎖和派生的子進(jìn)程數(shù)(如果是一個(gè)關(guān)系擴(kuò)展請(qǐng)求,arg1會(huì)是-1)。 arg2,arg3和arg4包含表空間,數(shù)據(jù)庫(kù)和關(guān)系OID,以識(shí)別關(guān)系。 arg5對(duì)本地緩沖區(qū)表示真,對(duì)共享緩沖區(qū)表示假。 arg6對(duì)關(guān)系擴(kuò)展請(qǐng)求表示真,對(duì)正常讀表示假。 |
buffer-read-done | (ForkNumber,BlockNumber,O id,O id,O id,bool,bool,bool) | 當(dāng)完成一次緩沖區(qū)讀時(shí)觸發(fā)的探測(cè)器。 arg0和arg1包含page塊中鎖和派生的子進(jìn)程數(shù)(如果是一個(gè)關(guān)系擴(kuò)展請(qǐng)求,arg1會(huì)表示新增鎖的數(shù)目)。 arg2,arg3和arg4包含表空間,數(shù)據(jù)庫(kù)和關(guān)系OID,以識(shí)別關(guān)系。 arg5對(duì)本地緩沖區(qū)表示真,對(duì)共享緩沖區(qū)表示假。 arg6對(duì)關(guān)系擴(kuò)展請(qǐng)求表示真,對(duì)正常讀表示假。 如果池中有緩沖區(qū),則arg7表示真,反之表示假。 |
buffer-flush-start | (ForkNumber,BlockNumber,O id,O id,O id) | 在發(fā)出共享緩沖區(qū)的任意寫(xiě)入請(qǐng)求時(shí)觸發(fā)的探測(cè)器。 arg0和arg1包含page塊中鎖和派生的子進(jìn)程數(shù)。 arg2,arg3和arg4包含表空間,數(shù)據(jù)庫(kù)和關(guān)系OID,以識(shí)別關(guān)系。 |
buffer-flush-done | (ForkNumber,BlockNumber,O id,O id,O id) | 當(dāng)完成一條寫(xiě)要求時(shí)觸發(fā)的探測(cè)器。 需要注意的是,它只影響將數(shù)據(jù)傳遞到內(nèi)核參數(shù)的時(shí)間; 實(shí)際上,它不會(huì)寫(xiě)到磁盤(pán)上。這個(gè)參數(shù)與buffer-flush-start一致。 |
buffer-write-dirty-start | (ForkNumber,BlockNumber,O id,O id,O id) | 當(dāng)服務(wù)器進(jìn)程開(kāi)始寫(xiě)臟緩沖區(qū)時(shí)觸發(fā)的探測(cè)電器。如果經(jīng)常發(fā)生,表示shared_buffers太小 ,或需要調(diào)整bgwriter控制參數(shù)。 arg0和arg1包含page塊中鎖和派生的子進(jìn)程數(shù)。 arg2,arg3和arg4包含表空間,數(shù)據(jù)庫(kù)和關(guān)系OID,以識(shí)別關(guān)系。 |
buffer-write-dirty-done | (ForkNumber,BlockNumber,O id,O id,O id) | 當(dāng)完成臟緩沖區(qū)寫(xiě)時(shí)觸發(fā)的探測(cè)器。 參數(shù)與buffer-write-dirty-start一樣。 |
wal-buffer-write-dirty-start | () | 當(dāng)服務(wù)器進(jìn)程開(kāi)始寫(xiě)臟讀時(shí)觸發(fā)的探測(cè)器(此時(shí)WAL緩沖區(qū)已滿(mǎn))。 如果經(jīng)常發(fā)生,應(yīng)該是wal_buffers設(shè)置的太小了。 |
wal-buffer-write-dirty-done | () | 當(dāng)完成一次WAL臟寫(xiě)時(shí)觸發(fā)的探測(cè)器。 |
xlog-insert | (unsignedchar,unsignedchar) | 當(dāng)插入一條WAL記錄時(shí)觸發(fā)的探測(cè)器。 arg0表示記錄的rm id。 arg1包含信息標(biāo)志。 |
xlog-switch | () | 當(dāng)要求進(jìn)行WAL切換時(shí)觸發(fā)的探測(cè)器。 |
smgr-md-read-start | (ForkNumber,BlockNumber,O id,O id,O id) | 開(kāi)始從一個(gè)關(guān)系中寫(xiě)鎖時(shí)觸發(fā)的探測(cè)器。 arg0和arg1包含page塊中鎖和派生的子進(jìn)程數(shù)。 arg2,arg3和arg4包含表空間,數(shù)據(jù)庫(kù)和關(guān)系OID,以識(shí)別關(guān)系。 |
smgr-md-read-done | (ForkNumber,BlockNumber,O id,O id,O id,int,int) | 當(dāng)一個(gè)鎖寫(xiě)完成時(shí)觸發(fā)的探測(cè)器。 arg0和arg1包含page塊中鎖和派生的子進(jìn)程數(shù)。 arg2,arg3和arg4包含表空間,數(shù)據(jù)庫(kù)和關(guān)系OID,以識(shí)別關(guān)系。 arg5表示實(shí)際讀的字節(jié)數(shù),而arg6表示要求的字節(jié)數(shù)(如果不一樣會(huì)報(bào)錯(cuò))。 |
smgr-md-write-start | (ForkNumber,BlockNumber,O id,O id,O id) | 當(dāng)向一個(gè)關(guān)系中寫(xiě)入鎖時(shí)觸發(fā)的探測(cè)器。 arg0和arg1包含page塊中鎖和派生子進(jìn)程數(shù)。 arg2,arg3和arg4包含表空間,數(shù)據(jù)庫(kù)和關(guān)系OID,以識(shí)別關(guān)系。 |
smgr-md-write-done | (ForkNumber,BlockNumber,O id,O id,O id,int,int) | 當(dāng)一個(gè)鎖寫(xiě)進(jìn)程完成時(shí)觸發(fā)的探測(cè)器。 arg0和arg1表示page塊的鎖和派生的子進(jìn)程數(shù)。 arg2,arg3和arg4包含表空間,數(shù)據(jù)庫(kù)和關(guān)系OID。 arg5表示實(shí)際寫(xiě)的字節(jié)數(shù),而arg6表示要求的數(shù)目(如果不一樣會(huì)報(bào)錯(cuò))。 |
sort-start | (int,bool,int,int,bool) | 排序操作開(kāi)始時(shí)觸發(fā)的探測(cè)器。 arg1對(duì)強(qiáng)制唯一值表示真。 arg2表示鍵列的數(shù)目。 arg3表示允許使用的內(nèi)存數(shù)目(以千字節(jié)為單位)。 如果要求隨機(jī)訪問(wèn)排序結(jié)果,那么arg4表示真。 |
sort-done | (bool,long) | 排序操作結(jié)束時(shí)觸發(fā)的探測(cè)器。 arg0對(duì)外部排序表示真,內(nèi)部排序表示假。 arg1表示用于一個(gè)外部排序的磁盤(pán)鎖的數(shù)目,或用于一個(gè)內(nèi)部排序的,以千字節(jié)為單位的內(nèi)存數(shù)目。 |
lwlock-acquire | (LWLockId,LWLockMode) | 當(dāng)成功獲得一個(gè)LWLock時(shí)觸發(fā)的探測(cè)器。 arg0是LWLock的ID號(hào),arg1表明請(qǐng)求的鎖的模式,要么獨(dú)占要么共享 |
lwlock-release | (LWLockId) | LWLock釋放時(shí)觸發(fā)的探測(cè)器。arg0表示LWLock的ID號(hào)。 |
lwlock-wait-start | (LWLockId,LWLockMode) | 當(dāng)不能立即獲得LWLock鎖,同時(shí)服務(wù)進(jìn)程進(jìn)入等待時(shí)觸發(fā)的探測(cè)器。 arg0是LWLock的ID號(hào),arg1表明請(qǐng)求的鎖的模式,要么獨(dú)占要么共享。 |
lwlock-wait-done | (LWLockId,LWLockMode) | 當(dāng)從一個(gè)LWLock鎖中釋放服務(wù)進(jìn)程時(shí)觸發(fā)的探測(cè)器(實(shí)際上沒(méi)有進(jìn)行鎖)。 arg0是LWLock的ID號(hào),arg1表明請(qǐng)求的鎖的模式,要么獨(dú)占要么共享。 |
lwlock-condacquire | (LWLockId,LWLockMode) | 當(dāng)成功獲得一個(gè)LWLock時(shí)觸發(fā)的探測(cè)器(已聲明調(diào)用無(wú)需等待)。 arg0是LWLock的ID號(hào),arg1表明請(qǐng)求的鎖的模式,要么獨(dú)占要么共享。 |
lwlock-condacquire-fail | (LWLockId,LWLockMode) | 當(dāng)沒(méi)有成功獲得一個(gè)LWLock時(shí)觸發(fā)的探測(cè)器(已聲明調(diào)用無(wú)需等待)。 arg0是LWLock的ID號(hào),arg1表明請(qǐng)求的鎖的模式,要么獨(dú)占要么共享。 |
lock-wait-start | (unsignedint,unsignedint,unsignedint,unsignedint,unsignedint,LOCKMODE) | 當(dāng)一個(gè)重量級(jí)鎖(lmgr鎖)的請(qǐng)求開(kāi)始等待(因?yàn)闊o(wú)法獲得鎖)時(shí)觸發(fā)的探測(cè)器。 arg0到arg3是辨別被鎖定對(duì)象的標(biāo)簽字段。arg4指出被鎖對(duì)象的類(lèi)型。 arg5表示請(qǐng)求的鎖類(lèi)型。 |
lock-wait-done | (unsignedint,unsignedint,unsignedint,unsignedint,unsignedint,LOCKMODE) | 當(dāng)一個(gè)重量級(jí)鎖(lmgr鎖)的請(qǐng)求結(jié)束等待時(shí)觸發(fā)的探測(cè)器,參數(shù)與lock-wait-start一樣。 |
deadlock-found | () | 當(dāng)死鎖探測(cè)器發(fā)現(xiàn)死鎖是觸發(fā)的探測(cè)器 |
Table 27-4. 定義用于探測(cè)器參數(shù)的類(lèi)型
類(lèi)型 | 定義 |
---|---|
LocalTransactionId | unsignedint |
LWLockId | int |
LWLockMode | int |
LOCKMODE | int |
BlockNumber | unsignedint |
O id | unsignedint |
ForkNumber | int |
bool | char |
下面的例子示范了一個(gè)分析事務(wù)次數(shù)的DTrace腳本,可以用來(lái)代替在性能測(cè)試 之前和之后的pg_stat_database快照。
#!/usr/sbin/dtrace-qs postgresql$1:::transaction-start { @start["Start"]=count(); self->ts=timestamp; } postgresql$1:::transaction-abort { @abort["Abort"]=count(); } postgresql$1:::transaction-commit /self->ts/ { @commit["Commit"]=count(); @time["Totaltime(ns)"]=sum(timestamp-self->ts); self->ts=0; }
例如示范D腳本執(zhí)行時(shí),如輸出:
#./txn_count.d`pgrep-npostgres`or./txn_count.d<PID> ^C Start71 Commit70 Totaltime(ns)2312105013
Note: SystemTap為跟蹤腳本使用一個(gè)不同的標(biāo)記而不是Dtrace,即使底層的跟蹤點(diǎn)是兼容的。 有一點(diǎn)需要注意,在這樣寫(xiě)的時(shí)候,SystemTap腳本必須使用雙下劃線代替連字符來(lái)指向探測(cè)器名。
必須在實(shí)際使用跟蹤程序前進(jìn)行仔細(xì)的編寫(xiě)和充分的調(diào)試,否則收集到的跟蹤信息可能毫 無(wú)意義。大多數(shù)問(wèn)題是由于外部跟蹤程序錯(cuò)誤導(dǎo)致的而不是底層系統(tǒng)。在討論使 用動(dòng)態(tài)跟蹤發(fā)現(xiàn)的信息時(shí),應(yīng)確保在其中包含你使用的跟蹤腳本。
更多的示例腳本,可以發(fā)現(xiàn)在PgFoundrydtraceproject.
開(kāi)發(fā)者可以在代碼中任意位置定義新的跟蹤點(diǎn),當(dāng)然這要重新編譯之后才能生效。下面是 用于新探測(cè)器插入步驟:
通過(guò)探頭決定探頭名字和數(shù)據(jù)可利用。
新增探頭定義為src/backend/utils/probes.d
包括pg_trace.h,如果已經(jīng)不在模塊中包含探測(cè)點(diǎn),并且在 所需源代碼中期望的位置插入TRACE_POSTGRESQL探測(cè)宏。
重新編譯和驗(yàn)證,新探頭是可用的
Example: 下面是一個(gè)例子,你將如何添加一個(gè)探頭,追蹤所有新的 交易通過(guò)事務(wù)ID。
決定,探測(cè)器將被命名為transaction-start并且 需要LocalTransactionId類(lèi)型參數(shù)
新增探頭定義為src/backend/utils/probes.d
probetransaction__start(LocalTransactionId);
注意在探測(cè)器名字中的雙下劃線。在一個(gè)DTrace使用探測(cè)器,需要用一個(gè)連字符來(lái)替換雙下滑線,因此 ,對(duì)用戶(hù)而言,transaction-start是文檔名。
在編譯時(shí),transaction__start被轉(zhuǎn)換成一個(gè)宏調(diào)用TRACE_POSTGRESQL_TRANSACTION_START (注意這里是單下劃線),可以從pg_trace.h中獲得。將宏調(diào)用放在源代碼中的合適位置。 在這種情況下,類(lèi)似下面:
TRACE_POSTGRESQL_TRANSACTION_START(vx id.localTransactionId);
在重新編譯和運(yùn)行新的二進(jìn)制文件之后,通過(guò)運(yùn)行下面的DTrace命令來(lái)檢查新增的探測(cè)器是否可用。 應(yīng)該得到類(lèi)似下面的結(jié)果:
#dtrace-lntransaction-start IDPROVIDERMODULEFUNCTIONNAME 18705postgresql49878postgresStartTransactionCommandtransaction-start 18755postgresql49877postgresStartTransactionCommandtransaction-start 18805postgresql49876postgresStartTransactionCommandtransaction-start 18855postgresql49875postgresStartTransactionCommandtransaction-start 18986postgresql49873postgresStartTransactionCommandtransaction-start
向C代碼中添加跟蹤宏時(shí),有一些注意事項(xiàng),見(jiàn)下文。
需要注意的是,為探測(cè)器參數(shù)聲明的數(shù)據(jù)類(lèi)型要匹配宏中可用的數(shù)據(jù)類(lèi)型,否則會(huì)發(fā)生編譯錯(cuò)誤。
在大多數(shù)平臺(tái)上,如果編譯PostgreSQL時(shí)帶有--enable-dtrace選項(xiàng), 無(wú)論何時(shí)通過(guò)宏來(lái)控制時(shí),都會(huì)估算該跟蹤宏的參數(shù),即使沒(méi)有進(jìn)行跟蹤。通常不需要擔(dān)心是否你只是報(bào)告一些局部變量的值。 但要注意將重要的函數(shù)調(diào)用放置在參數(shù)中。如果需要這么做,考慮通過(guò)檢查跟蹤是否真的開(kāi)啟的檢查來(lái)保護(hù)宏:
if(TRACE_POSTGRESQL_TRANSACTION_START_ENABLED()) TRACE_POSTGRESQL_TRANSACTION_START(some_function(...));
每個(gè)跟蹤宏都有一個(gè)相應(yīng)的ENABLED宏。