在程式中直接中斷
依《Binary Hack》 #92 所言, 在 gdb 監控 process 的情況, 使用下列程式碼會進入中斷:
raise(SIGTRAP);
對 x86 環境, 下列作法也許會更好, 少了一層 function call
__asm__ __volatile__("int3");
Scott 有個 debugbreak 有處理 x86 以外的情況, 有需要時再來參考看看。
偵測是否被 gdb 監控中
比起在程式中直接中斷, 我比較需要知道目前程式是否被 gdb 監控 (attach) 中。以下是兩個實用的情境:
- 原本已有 log 錯誤訊息, 在 log 極為嚴重錯誤訊息 (如 severity = FATAL) 時, 檢查看看是否有 gdb 在監控, 有的話不如順便進入中斷, 提前察看錯誤點。
- 寫 multi-thread 時, 有時會有特定 thread 負責特定的事, 像是 UI 或 IO。有時會監控這個 thread 是否正常運作。或是有網路連線時, 有時會想監控另一端連線是否仍然正常運作。其中一種監控 thread 或網路連線的方式是週期性察看有無更新資料, 超過一定時間沒更新, 就中止程序或關掉連線重連。在使用 gdb 除錯時, 有可能在中斷點待太久而觸發前述安全防護機制, 反而無法長時間除錯。這時自動關閉防護措施會比較方便。
《Binary Hack》 #92 的作法利用設 gdb 會覆寫 SIGTRAP 的 signal handler 做檢查, 這個作法可以跨比較多平台, 但可用時機太窄。我偏好 Chromium 的作法, 直接讀 /proc/PID/status, 由 "TracerPid" 的值得知目前是否有 process 控控自己。程式執行中的任何時間都可用這方法得知正確的結果, 不過只適用於 Linux 和 Android。以下是參考 Chromium 實作稍作修改的版本:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Ref. chromium/src/base/debug/debugger_posix.cc | |
#include <errno.h> | |
#include <fcntl.h> | |
#include <string.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <unistd.h> | |
#include <iostream> | |
#define HANDLE_EINTR(x) ({ \ | |
typeof(x) __eintr_result__; \ | |
do { \ | |
__eintr_result__ = (x); \ | |
} while (__eintr_result__ == -1 && errno == EINTR); \ | |
__eintr_result__;\ | |
}) | |
bool BeingDebugged() { | |
// NOTE: This code MUST be async-signal safe (it's used by in-process | |
// stack dumping signal handler). NO malloc or stdio is allowed here. | |
int status_fd = open("/proc/self/status", O_RDONLY); | |
if (status_fd == -1) | |
return false; | |
// We assume our line will be in the first 1024 characters and that we can | |
// read this much all at once. In practice this will generally be true. | |
// This simplifies and speeds up things considerably. | |
char buf[1024]; | |
ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf))); | |
if (HANDLE_EINTR(close(status_fd)) < 0) | |
return false; | |
if (num_read <= 0) | |
return false; | |
const char *target = "TracerPid:\t"; | |
char *found = strstr(buf, target); | |
if (!found) | |
return false; | |
// Our pid is 0 without a debugger, assume this for any pid starting with 0. | |
int index = found - buf + strlen(target); | |
return index < num_read && buf[index] != '0'; | |
} | |
int main(void) { | |
while (true) { | |
sleep(1); | |
std::cout << getpid() << " is debugged? " << BeingDebugged() << std::endl; | |
} | |
return 0; | |
} |
沒有留言:
張貼留言