sys.monitoring --- 執行事件監控

在 3.12 版被加入.


備註

sys.monitoringsys 模組內的一個命名空間,不是一個獨立的模組,所以不需要 import sys.monitoring,只需 import sys 然後使用 sys.monitoring

此命名空間提供對啟動和控制事件監控所需的函式和常數的存取。

當程式執行時,會發生一些能被監控工具關注的事件。sys.monitoring 命名空間提供了在發生欲關注事件時接收回呼的方法。

監控 API 由三個元件組成:

工具識別器

工具識別器是一個整數和關聯的名稱。工具識別器用於防止工具相互干擾並允許多個工具同時運作。目前工具是完全獨立的,不能用來互相監控。將來此限制可能會取消。

在註冊或啟動事件之前,工具應選擇一個識別器。識別器是 0 到 5(含)範圍內的整數。

註冊和使用工具

sys.monitoring.use_tool_id(tool_id: int, name: str, /) None

必須在使用 tool_id 之前呼叫。tool_id 必須在 0 到 5(含)範圍內。如果 tool_id 正在使用,則引發 ValueError

sys.monitoring.clear_tool_id(tool_id: int, /) None

Unregister all events and callback functions associated with tool_id.

sys.monitoring.free_tool_id(tool_id: int, /) None

Should be called once a tool no longer requires tool_id. Will call clear_tool_id() before releasing tool_id.

sys.monitoring.get_tool(tool_id: int, /) str | None

如果 tool_id 正在使用,則回傳該工具的名稱,否則回傳 Nonetool_id 必須在 0 到 5(含)範圍內。

對於事件,虛擬機器對所有 ID 的處理都是相同的,但預先定義了以下 ID,以使工具間的協作更容易:

sys.monitoring.DEBUGGER_ID = 0
sys.monitoring.COVERAGE_ID = 1
sys.monitoring.PROFILER_ID = 2
sys.monitoring.OPTIMIZER_ID = 5

事件

支援以下事件:

sys.monitoring.events.BRANCH

採取(或不採取)一個條件分支。

sys.monitoring.events.CALL

Python 程式碼中的呼叫(事件發生在呼叫之前)。

sys.monitoring.events.C_RAISE

從任何可呼叫物件引發的例外,除 Python 函式外(事件發生在退出之後)。

sys.monitoring.events.C_RETURN

從任何可呼叫函式回傳,除 Python 函式外(事件發生在回傳之後)。

sys.monitoring.events.EXCEPTION_HANDLED

一個例外被處理。

sys.monitoring.events.INSTRUCTION

虛擬機器指令即將被執行。

sys.monitoring.events.JUMP

在控制流程圖中進行無條件跳轉。

sys.monitoring.events.LINE

即將執行的指令與前一條指令的列號不同。

sys.monitoring.events.PY_RESUME

Python 函式的繼續執行(對於產生器和協程函式),除了 throw() 呼叫。

sys.monitoring.events.PY_RETURN

從 Python 函式回傳(發生在即將回傳之前,被呼叫者的 frame 將位於堆疊上)。

sys.monitoring.events.PY_START

Python 函式的開始(在呼叫後立即發生,被呼叫者的 frame 將位於堆疊上)

sys.monitoring.events.PY_THROW

Python 函式透過 throw() 呼叫來繼續。

sys.monitoring.events.PY_UNWIND

在例外展開 (unwind) 期間從 Python 函式退出。

sys.monitoring.events.PY_YIELD

來自 Python 函式的 yield(在即將 yield 之前發生,被呼叫者的 frame 將位於堆疊上)。

sys.monitoring.events.RAISE

例外被引發,除了那些會導致 STOP_ITERATION 的事件外。

sys.monitoring.events.RERAISE

例外被重新引發 (re-raise),例如在 finally 區塊的最後。

sys.monitoring.events.STOP_ITERATION

一個人為的 StopIteration 被引發;請參閱 STOP_ITERATION 事件

將來可能會新增更多事件。

這些事件是 sys.monitoring.events 命名空間的屬性。每個事件以 2 次方的整數常數表示。要定義一組事件,只要將個別事件以 bitwise or 相接即可。例如,若要指定 PY_RETURNPY_START 兩個事件,請使用運算式 PY_RETURN | PY_START

sys.monitoring.events.NO_EVENTS

0 的別名,以便使用者可以進行明確比較,例如:

if get_events(DEBUGGER_ID) == NO_EVENTS:
    ...

事件被分為三組:

區域事件

區域事件與程式的正常執行相關,並發生在明確定義的位置。所有區域事件都可以被停用。區域事件有:

附屬事件 (ancillary events)

附屬事件可以像其他事件一樣被監控,但由另一個事件所控制:

C_RETURNC_RAISE 事件由 CALL 事件控制。只有當對應的 CALL 事件受到監控時,才會看到 C_RETURNC_RAISE 事件。

其他事件

其他事件不一定與程式中的特定位置相關聯,也不能單獨停用。

其他可以監控的事件有:

STOP_ITERATION 事件

PEP 380 指出從產生器或協程回傳值時要引發 StopIteration 例外。然而,這是一種非常低效的回傳值方式,因此有一些 Python 實作(特別是 CPython 3.12+)不會引發例外,除非它對其他程式碼是可見的。

為了允許工具去監控真正的例外而不減慢產生器和協程的速度,提供了 STOP_ITERATION 事件。與 RAISE 不同,STOP_ITERATION 可以區域停用。

Note that the STOP_ITERATION event and the RAISE event for a StopIteration exception are equivalent, and are treated as interchangeable when generating events. Implementations will favor STOP_ITERATION for performance reasons, but may generate a RAISE event with a StopIteration.

開啟和關閉事件

為了監控一個事件,必須打開它並註冊相應的回呼。可以透過將事件設定為全域或只為特定程式碼物件來開啟或關閉事件。

全域設定事件

可以透過修改正在監控的事件集合來全域地控制事件。

sys.monitoring.get_events(tool_id: int, /) int

回傳代表所有有效事件的 int

sys.monitoring.set_events(tool_id: int, event_set: int, /) None

啟動 event_set 中設定的所有事件。如果 tool_id 並未正在被使用,則引發 ValueError

預設沒有有效事件。

各別程式碼物件事件

事件還可以基於各別程式碼物件進行控制。下面定義的、接受 types.CodeType 的函式應該準備好接受來自 Python 中未定義函式的類似物件(請參閱 Monitoring C API)。

sys.monitoring.get_local_events(tool_id: int, code: CodeType, /) int

回傳 code 的所有區域事件

sys.monitoring.set_local_events(tool_id: int, code: CodeType, event_set: int, /) None

啟動 event_set 中針對 code 設定的所有區域事件。如果 tool_id 並未正在被使用,則引發 ValueError

區域事件會加入到全域事件中,但不會掩蓋它們。換句話說,無論區域事件如何,所有全域事件都將為程式碼物件觸發。

停用事件

sys.monitoring.DISABLE

可以從回呼函式回傳的特殊值,以停用當前程式碼位置的事件。

可透過回呼函式回傳 sys.monitoring.DISABLE 來停用特定程式碼位置的區域事件。這不會改變被設定的事件,或相同事件的任何其他程式碼位置。

停用特定位置的事件對於高效能監控非常重要。舉例來說,如果除少數斷點外,偵錯器可以停用所有監控,那麼在偵錯器下執行程式就不會有任何開銷 (overhead)。

sys.monitoring.restart_events() None

為所有工具啟用由 sys.monitoring.DISABLE 停用的所有事件。

註冊回呼函式

用來註冊對事件呼叫的可呼叫物件

sys.monitoring.register_callback(tool_id: int, event: int, func: Callable | None, /) Callable | None

使用給定的 tool_id 來註冊對 event 的可呼叫物件 func

如果給定的 tool_idevent 已經註冊了另一個回呼,則會取消註冊並回傳。否則 register_callback() 會回傳 None

可以透過呼叫 sys.monitoring.register_callback(tool_id, event, None) 來取消註冊函式。

回呼函式可以隨時被註冊和取消註冊。

註冊或取消註冊回呼函式將產生 sys.audit() 事件。

回呼函式引數

sys.monitoring.MISSING

傳遞給回呼函式的特殊值,表示該呼叫沒有引數。

當有效事件發生時,已註冊的回呼函式會被呼叫。不同的事件會為回呼函式提供不同的引數,如下所示:

  • PY_STARTPY_RESUME

    func(code: CodeType, instruction_offset: int) -> DISABLE | Any
    
  • PY_RETURNPY_YIELD

    func(code: CodeType, instruction_offset: int, retval: object) -> DISABLE | Any
    
  • CALLC_RAISEC_RETURN

    func(code: CodeType, instruction_offset: int, callable: object, arg0: object | MISSING) -> DISABLE | Any
    

    如果沒有引數,arg0 將被設定為 sys.monitoring.MISSING

  • RAISERERAISEEXCEPTION_HANDLEDPY_UNWINDPY_THROWSTOP_ITERATION

    func(code: CodeType, instruction_offset: int, exception: BaseException) -> DISABLE | Any
    
  • LINE

    func(code: CodeType, line_number: int) -> DISABLE | Any
    
  • BRANCHJUMP

    func(code: CodeType, instruction_offset: int, destination_offset: int) -> DISABLE | Any
    

    請注意,destination_offset 是程式碼接下來要執行的地方。對於未採用的分支,這將是該分支之後的指令的偏移量。

  • INSTRUCTION

    func(code: CodeType, instruction_offset: int) -> DISABLE | Any