共用方式為


具有標記的物件參考追蹤

核心物件 是 Windows 核心在系統記憶體中實作的基本資料物件。 它們代表裝置、驅動程式、檔案、登錄機碼、事件、旗號、進程和執行緒等實體。

大部分的核心物件不是永久的。 為了避免 Windows 在使用核心模式驅動程式時刪除非Permanent 核心物件,驅動程式會取得物件的計數參考。 當驅動程式不再需要 物件時,驅動程式會釋放其物件參考。

如果驅動程式未釋放物件的所有參考,物件的參考計數永遠不會達到零,而物件管理員永遠不會刪除它。 在作業系統重新開機之前,您無法重複使用流失的資源。

如果參考 物件的驅動程式 就會發生另一種類型的參考錯誤。 在此情況下,驅動程式會釋出比驅動程式實際保留更多的物件參考。 此錯誤可能會導致物件管理員提前刪除物件,而其他用戶端仍嘗試存取物件。

核心物件的外泄和參考不足可能是難以追蹤的錯誤。 例如,進程物件或裝置物件可能有數千個參考。 在這些情況下,很難識別物件參考 Bug 的來源。

在 Windows 7 和更新版本的 Windows 中,您可以提供物件參考的標記,讓這些 Bug 更容易找到。 下列常式會將標籤與核心物件的參考取得和發行建立關聯:

ObDereferenceObjectDeferDeleteWithTag

ObDereferenceObjectWithTag

ObReferenceObjectByHandleWithTag

ObReferenceObjectByPointerWithTag

ObReferenceObjectWithTag

例如,在 Windows 7 和更新版本中提供的 ObReferenceObjectWithTagObDereferenceObjectWithTag是 Windows 2000 和更新版本的 ObReferenceObjectObDereferenceObject 常式的增強版本。 這些增強常式可讓您提供四位元組的自訂標籤值作為輸入參數。 您可以使用 Windows 偵錯工具來 檢查 物件參考追蹤 ,其中包含每個呼叫 的標記值。 ObReferenceObjectObDereferenceObject 不會讓呼叫端指定自訂標籤,但在 Windows 7 和更新版本中,這些常式會將具有標籤值 「Dflt」) 的預設標籤新增至追蹤 (。 因此,對 ObReferenceObjectObDereferenceObject 的呼叫與對 ObReferenceObjectWithTagObDereferenceObjectWithTag 的呼叫相同,指定 「Dflt」 的標記值。 (在您的程式中,此標記值會顯示為 0x746c6644 或 'tlfD'.)

若要追蹤潛在的物件外泄或參考不足,請在驅動程式中識別一組相關聯的 ObReferenceObjectXxxWithTagObDereferenceObjectXxxWithTag 呼叫,以遞增和遞減特定物件的參考計數。 (選擇一般標籤值,例如「Lky8」) 用於此集合中的所有呼叫。 當您的驅動程式使用 物件完成之後,遞減的數目應該完全符合遞增的數目。 如果這些數位不相符,您的驅動程式就會有物件參考 Bug。 偵錯工具可以比較每個標記值的遞增和遞減數目,並告訴您它們是否不相符。 使用這項功能,您可以快速找出參考計數不符的來源。

若要在 Windows 偵錯工具中檢視物件參考追蹤,請使用 !obtrace 核心模式偵錯工具延伸模組。 如果物件參考追蹤開啟,您可以使用 !obtrace 延伸模組來顯示物件參考標記。 根據預設,物件參考追蹤已關閉。 使用 全域旗標編輯器 (Gflags) 來啟用物件參考追蹤。 如需 Gflags 的詳細資訊,請參閱 設定物件參考追蹤

!obtrace延伸模組的輸出包含 「Tag」 資料行,如下列範例所示:

0: kd> !obtrace 0x8a226130
Object: 8a226130
 Image: leakyapp.exe
Sequence   (+/-)   Tag    Stack
--------   -----   ----   --------------------------------------------
      36    +1     Dflt      nt!ObCreateObject+1c4
                             nt!NtCreateEvent+93
                             nt!KiFastCallEntry+12a

      37    +1     Dflt      nt!ObpCreateHandle+1c1
                             nt!ObInsertObjectEx+d8
                             nt!ObInsertObject+1e
                             nt!NtCreateEvent+ba
                             nt!KiFastCallEntry+12a

      38    -1     Dflt      nt!ObfDereferenceObjectWithTag+22
                             nt!ObInsertObject+1e
                             nt!NtCreateEvent+ba
                             nt!KiFastCallEntry+12a

      39    +1     Lky8      nt!ObReferenceObjectByHandleWithTag+254
                             leakydrv!LeakyCtlDeviceControl+6c
                             nt!IofCallDriver+63
                             nt!IopSynchronousServiceTail+1f8
                             nt!IopXxxControlFile+6aa
                             nt!NtDeviceIoControlFile+2a
                             nt!KiFastCallEntry+12a

      3a    -1     Dflt      nt!ObfDereferenceObjectWithTag+22
                             nt!ObpCloseHandle+7f
                             nt!NtClose+4e
                             nt!KiFastCallEntry+12a
 
--------   -----   ----   --------------------------------------------
References: 3, Dereferences 2
Tag: Lky8 References: 1 Dereferences: 0 Over reference by: 1

此範例中的最後一行表示與 「Lky8」 標記相關聯的參考和取值計數不相符,而且此不相符的結果是由一個 (過度參考,也就是流失) 。

如果結果改為參考不足, !obtrace 輸出的最後一行可能如下所示:

Tag: Lky8 References: 1 Dereferences: 2 Under reference by: 1

根據預設,作業系統會在釋放物件之後刪除物件的物件參考追蹤,以節省記憶體。 即使系統釋放物件之後,仍可將追蹤保留在記憶體中,在追蹤參考不足時也很有用。 為了達到此目的,Gflags 工具會提供「永久」選項,可在電腦關閉後再次啟動時,保留記憶體中的追蹤。

Windows XP 引進了物件參考追蹤。 由於一開始追蹤未包含標記,因此開發人員必須使用較不方便的技術來識別物件參考 Bug。 偵錯工具可以追蹤物件群組的參考,而開發人員依物件類型所選取。 開發人員唯一可以識別物件參考和取值的各種來源,就是比較其呼叫堆疊。 雖然先前 的 !obtrace 範例只包含五個堆疊,但某些類型的物件,例如 程式 (EPROCESS) 物件,可能會被參考並取值數千次。 使用數千個堆疊來檢查時,可能很難識別物件外泄或參考不足的來源,而不使用標記。