簡介
當執行緒遇到錯誤狀況,例如分段錯誤時 且執行緒會進入例外狀況處理。包含 註冊收到這些例外狀況的通知,並有機會 檢查或修正條件
這項功能經常被偵錯工具或當機記錄器使用 有機會與執行緒互動,以免發生其他當機情況。 適用於只想追蹤任務生命週期的應用程式, 攔截當機,信號可能較適合您。
基本概念
藉由在
透過 zx_task_create_exception_channel()
執行任務 (執行緒、程序或工作)
系統呼叫。建立的帳號代碼是標準的 Zircon
channel,但以唯讀方式建立,因此只能使用
用於接收例外狀況訊息
發生例外狀況時,執行緒就會暫停,而訊息會包含
zx_exception_info_t
和例外狀況帳號代碼會傳送至該管道。
例外狀況的生命週期是這個例外狀況控制代碼的生命週期,所以
接收器處理完畢後,關閉這個例外狀況處理常式就會繼續執行
例外狀況。這個例外狀況帳號代碼不可複製,也就是說,任何
這個例外狀況只有一個處理常式。
根據預設,關閉例外狀況控點會讓執行緒暫停並傳送
下一個處理常式除外如果接收方已修正例外狀況
且希望執行緒改為繼續執行,則可以變更例外狀況
狀態透過 zx_object_set_property()
傳送給 ZX_EXCEPTION_STATE_HANDLED
關閉中。
例外狀況帳號代碼
例外狀況處理的方式類似於暫停權杖,也就是保留執行緒 直至關閉為止此外,例外狀況控點具備 ,以便接收端處理例外狀況:
- 使用
zx_object_set_property()
搭配ZX_PROP_EXCEPTION_STATE
即可設定行為 帳號代碼關閉 zx_exception_get_thread()
,取得例外狀況執行緒的控制代碼zx_exception_get_process()
取得例外狀況程序的控制代碼 (僅限處理程序或工作例外狀況管道)
從例外狀況擷取的工作控點具備與工作相同的權限
原本傳遞至 zx_task_create_exception_channel()
。
範例
這個簡單的範例會建立例外狀況管道,並循環讀取例外狀況 直到工作關閉為止
void ExceptionHandlerLoop(zx_handle_t task) {
// Create the exception channel.
uint32_t options = 0;
zx_handle_t channel;
zx_status_t status = zx_task_create_exception_channel(task, options,
&channel);
// ... check status ...
while (true) {
// Wait until we get ZX_CHANNEL_READABLE (exception) or
// ZX_CHANNEL_PEER_CLOSED (task terminated).
zx_signals_t signals = 0;
status = zx_object_wait_one(channel,
ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED,
ZX_TIME_INFINITE, &signals);
// ... check status ...
if (signals & ZX_CHANNEL_READABLE) {
// Read the exception info and handle from the channel.
zx_exception_info_t info;
zx_handle_t exception;
status = zx_channel_read(channel, 0, &info, &exception, sizeof(info), 1,
nullptr, nullptr);
// ... check status ...
// Send the exception out to some other function for processing, which
// returns true if the exception has been handled and we can resume the
// thread, or false to pass the exception to the next handler.
bool handled = process_exception(info, exception);
if (handled) {
uint32_t state = ZX_EXCEPTION_STATE_HANDLED;
status = zx_object_set_property(exception, ZX_PROP_EXCEPTION_STATE,
&state, sizeof(state));
// ... check status ...
}
// Close the exception to finish handling.
zx_handle_close(exception);
} else {
// We got ZX_CHANNEL_PEER_CLOSED, the task has terminated.
zx_handle_close(channel);
return;
}
}
}
例外狀況類型
大致上有兩種例外狀況:架構和合成。 架構例外是指分段錯誤 (例如將 NULL 指標) 或執行未定義的指令。合成例外狀況 例如執行緒開始/停止通知 政策違規。
我們會將架構和政策例外狀況視為嚴重例外狀況,導致 系統就會在未處理的情況下終止這個程序僅限偵錯工具的例外狀況 - 執行緒 開始/停止及程序開始 - 提供資訊,且會繼續執行 通常會在執行緒未明確恢復的情況下繼續。這些例外狀況 目的是讓偵錯工具能正確回應這些生命週期事件 因為相應的執行緒會暫停,直到例外狀況恢復為止。
例外狀況類型定義於
<zircon/syscalls/exception.h>
。
例外狀況管道類型
例外狀況管道的特性因工作類型及工作類型而異
ZX_EXCEPTION_CHANNEL_DEBUGGER
旗標是否會傳遞至
zx_task_create_exception_channel()
。下表摘要列出
各種管道類型的差別:
頻道類型 | get_thread |
get_process |
建築政策例外狀況 | 執行緒啟動/停止例外狀況 | 程序啟動例外狀況 |
---|---|---|---|---|---|
執行緒 | X | X | |||
程序 | X | X | X | ||
程序偵錯工具 | X | X | X | X | |
工作 | X | X | X | ||
工作偵錯工具 | X | X | X |
管道類型也會決定例外狀況管道的發生順序 有機會處理例外狀況:
- 程序偵錯工具
- 執行緒
- 程序
- 程序偵錯工具 (如果例外狀況為
'second-chance'
,則可選擇是否使用) - 工作 (父項工作 -> 祖父母工作 -> 等)
如果沒有嘗試剩餘的例外狀況管道,核心會終止
處理過程就好像呼叫 zx_task_kill()
時一樣。程序的傳回代碼
就會發生例外狀況終止為 ZX_TASK_RETCODE_EXCEPTION_KILL
使用 ZX_INFO_PROCESS
透過 zx_object_get_info()
取得。
每項工作都只支援一個例外狀況管道,例如: 指定的程序已附加偵錯例外狀況管道 第二個偵錯例外狀況管道會失敗,但會建立非偵錯管道 才能成功
ZX_EXCP_PROCESS_STARTING
和工作偵錯工具管道
ZX_EXCP_PROCESS_STARTING
的行為與其他例外狀況不同。是
只會傳送至工作偵錯工具例外狀況管道,而且一律會傳送給所有已找到的工作
處理常式,基本上會假設 ZX_EXCEPTION_STATE_TRY_NEXT
實際處理常式行為這也是唯一由核心定義的例外狀況
工作偵錯工具管道接收,使其成為偵測工作負載的特殊案例處理常式
新的處理程序工作偵錯工具也可能收到 ZX_EXCP_USER
例外狀況
可以透過 zx_thread_raise_exception()
syscall 引發。
系統會將工作偵錯工具管道視為「唯讀」,因此多個工作偵錯工具
頻道 (最多 ZX_EXCEPTION_CHANNEL_JOB_DEBUGGER_MAX_COUNT
個) 可在以下位置建立:
一項工作如果某個工作建立了多個偵錯管道,
ZX_EXCP_PROCESS_STARTING
事件將依序傳送至所有管道,
並通知先前建立的頻道。
使用者定義的例外狀況
zx_thread_raise_exception()
系統呼叫可用於提出使用者定義的
例外狀況。這些例外狀況的類型為 ZX_EXCP_USER
,亦即使用者定義
產生在以下兩個欄位的 synth_code
和 synth_data
欄位中的值
zx_exception_context_t
。目前,可以傳送使用者定義的例外狀況
使用於工作偵錯工具的例外狀況管道。
先處理偵錯工具,可能之後再處理
在 Zircon 中,系統會先嘗試執行程序偵錯工具例外狀況管道。這很實用 至少由下列幾個原因造成:
- 允許「修正並繼續」偵錯,例如如果執行緒發生分段錯誤 偵錯工具使用者可以修正隔離錯誤,並在不使用任何指令的情況下恢復執行緒 非偵錯工具管道看到例外狀況。
- 確保偵錯工具的中斷點直接傳送至偵錯工具,而沒有 就必須明確傳遞這些物件
如果例外狀況已設定 ZX_EXCEPTION_STRATEGY_second_CHANCE 並維持 嘗試執行程序例外狀況管道後未處理的情況,那麼程序偵錯工具 則有更多機會這項技術的功用為 程序會監聽自身的例外狀況 確保正常運作的資訊在這個例子中 會在修正失敗事件時執行檢查。
與工作停權互動
例外狀況和執行緒停權會分別處理。 換句話說,執行緒可能屬於例外狀況,並會暫停。 如果執行緒在等候回應時遭到停權,就可能會發生這種情況 來自例外狀況處理常式執行緒在恢復之前會保持暫停狀態 例外狀況和停權狀態:
zx_handle_close(exception);
zx_handle_close(suspend_token);
順序沒有任何影響。
與工作終止互動
zx_task_kill()
會停止工作上的任何例外狀況處理。如果應用程式在 2014 年
執行緒 (或其父項程序/工作) 遭到例外狀況時:
- 執行緒將會停止等候目前的例外狀況處理常式
- 再也不會收到例外狀況
zx_exception_get_thread()
和zx_exception_get_process()
: 未完成的例外狀況帳號代碼會繼續提供有效的工作控制代碼- 用來設定例外狀況狀態的
zx_object_set_property()
,仍會傳回ZX_OK
,但狀態不會產生任何影響,因為執行緒不再 封鎖處理常式
此外,終止的執行緒仍會傳送 ZX_EXCP_THREAD_EXITING
例外狀況 (註冊了程序偵錯處理常式),但如上所述,則不會
等待處理常式回應。
雖然 zx_task_kill()
通常為非同步性質,但這樣即使執行緒
未在 Syscall 傳回時完成終止,則會同步執行
停止例外狀況處理,在傳回後關閉例外狀況控點
不會繼續執行執行緒,也不會將例外狀況傳遞至其他處理常式。
訊號
信號是觀察狀態變更的核心 Zircon 機制 (變為可讀取的管道、程序終止、 訊號等
與例外狀況不同,信號不需要例外狀況處理常式的回應。 另一種方式則是將訊號傳送給在討論串的 處理常式,而不是傳送到可能繫結至 執行緒的處理程序。
Zircon 的常見模式是採用等待訊息迴圈的訊息迴圈
一或多個物件,並即時處理。如何納入例外狀況
處理中,請使用 zx_object_wait_async()
等待
ZX_CHANNEL_READABLE
(以及選用的 ZX_CHANNEL_PEER_CLOSED
)
例外管道:
zx_handle_t port;
zx_status_t status = zx_port_create(0, &port);
// ... check status ...
// Start waiting on relevant signals on the exception channel.
status = zx_object_wait_async(exception_channel, port, kMyExceptionKey,
ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, 0);
// ... check status ...
// ... add other objects to |port| with wait_async() ...
while (1) {
zx_port_packet_t packet;
status = zx_port_wait(port, ZX_TIME_INFINITE, &packet);
// ... check status ...
if (packet.key == kMyExceptionKey) {
if (packet.signal.observed & ZX_CHANNEL_READABLE) {
// ... extract exception from |exception_channel| and process it ...
// wait_async() is one-shot so we need to reload it to continue
// receiving signals.
status = zx_object_wait_async(
exception_channel, port, kMyExceptionKey,
ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED, 0);
// ... check status ...
} else {
// Got ZX_CHANNEL_PEER_CLOSED, task has terminated.
zx_handle_close(exception_channel);
}
} else {
// ... handle other objects added to |port| ...
}
}
與 Posix (和 Linux) 的比較
此表格顯示了彼此之間的等效字詞、類型和函式呼叫 Zircon 和 Posix/Linux 的例外狀況與例外狀況 處理常式通常都會這麼做
Zircon | Posix/Linux |
---|---|
例外狀況/信號 | 信號 |
ZXEXCP* | SIG* |
zx_task_create_exception_channel() | ptrace(ATTACH,DETACH) |
zx_task_suspend() | kill(SIGSTOP),ptrace(KILL(SIGSTOP)) |
zx_handle_close(suspend_token) | kill(SIGCONT)、ptrace(CONT) |
zx_handle_close(exception) | kill(SIGCONT)、ptrace(CONT) |
zx_task_kill() | kill(SIGKILL) |
zx_thread_raise_exception() | 殺害(SIGUSR1) |
無 | kill(everything_else) |
未定 | signal()/sigaction() |
zx_port_wait() | wait*() |
多項 | sys/wait.h 提供的 W*() 巨集 |
zx_exception_info_t | siginfo_t |
zx_exception_context_t | siginfo_t |
zx_thread_read_state() | ptrace(GETREGS,GETREGSET) |
zx_thread_write_state() | ptrace(SETREGS,SETREGSET) |
zx_process_read_memory() | ptrace(PEEKTEXT) |
zx_process_write_memory() | ptrace(POKETEXT) |
Zircon 沒有重大的非同步信號,例如 SIGINT
、SIGQUIT
、SIGTERM
、
SIGUSR1
、SIGUSR2
等。
與 Posix 的另一個重大差異 在於 Zircon 中的執行緒無法 因為 Zircon 例外狀況處理是同步的 作業是由使用者空間驅動,而不是由 與核心部分相同
範例
這裡可進一步查看使用例外狀況的 Zircon 程式碼,包括:
src/bringup/bin/pwrbtn-monitor/crashsvc
:系統層級的當機處理常式system/utest/exception
:例外狀況單元測試system/utest/debugger
:偵錯工具相關功能單元測試
另請參閱
zx_task_create_exception_channel()
zx_exception_get_thread()
zx_exception_get_process()
zx_object_set_property()
zx_port_wait()