我想創建一個程序的執行的完整指令跟蹤,收集一些統計等。我第一次嘗試使用linux'ptrace功能來完成一個程序(使用教程here)。這創建了兩個進程,追蹤的和調試器,並通過信號進行通信。我每秒只有16K條指令(在1.6GHz的Atom上),所以對於任何不重要的東西來說這太慢了。試圖單步通過程序與陷阱國旗和陷阱信號處理程序,崩潰vsyscall
我認爲通過信號的進程間通信太慢了,所以我嘗試在執行的同一個進程中設置調試:設置陷阱標誌,並創建一個信號處理程序。當使用軟件中斷進行系統調用時,應該保存陷阱標誌,內核將使用它自己的標誌 - 所以我想。但是我的程序以某種方式被信號SIGTRAP殺死。
這是我設置:
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
int cycle = 0;
void trapHandler(int signum) {
if (cycle % 262144 == 0) {
write(STDOUT_FILENO," trap\n",6);
}
cycle += 1;
}
void startTrace() {
// set up signal handler
signal(SIGTRAP, trapHandler);
// set trap flag
asm volatile("pushfl\n"
"orl $0x100, (%esp)\n"
"popfl\n"
);
}
void printRock() {
char* s = "Rock\n";
asm(
"movl $5, %%edx\n" // message length
"movl %0, %%ecx\n" // message to write
"movl $1, %%ebx\n" // file descriptor (stdout)
"movl $4, %%eax\n" // system call number (sys_write)
"int $0x80\n" // sycall
: // no output regs
: "r"(s) // input text
: "edx","ecx","ebx","eax"
);
}
int main() {
startTrace();
// some computation
int x = 0;
int i;
for (i = 0; i < 100000; i++) {
x += i*2;
}
printRock();
write(STDOUT_FILENO,"Paper\n",6);
write(STDOUT_FILENO,"Scissors\n",9);
}
運行時,這給:
trap
trap
trap
Rock
Paper
trap
Trace/breakpoint trap (core dumped)
所以,現在我們得到約每秒250K的指令,仍然緩慢,但不平凡的執行是可能的。但是在這兩個寫入調用之間似乎會發生核心轉儲。在GDB中,我們看到它發生在哪裏:
Dump of assembler code for function __kernel_vsyscall:
0xb76f3414 <+0>: push %ecx
0xb76f3415 <+1>: push %edx
0xb76f3416 <+2>: push %ebp
0xb76f3417 <+3>: mov %esp,%ebp
0xb76f3419 <+5>: sysenter
0xb76f341b <+7>: nop
0xb76f341c <+8>: nop
0xb76f341d <+9>: nop
0xb76f341e <+10>: nop
0xb76f341f <+11>: nop
0xb76f3420 <+12>: nop
0xb76f3421 <+13>: nop
0xb76f3422 <+14>: int $0x80
=> 0xb76f3424 <+16>: pop %ebp
0xb76f3425 <+17>: pop %edx
0xb76f3426 <+18>: pop %ecx
0xb76f3427 <+19>: ret
而且回溯:
Program terminated with signal SIGTRAP, Trace/breakpoint trap.
#0 0xb77c5424 in __kernel_vsyscall()
#1 0xb76d0553 in __write_nocancel() at ../sysdeps/unix/syscall-template.S:81
#2 0x0804847d in trapHandler (signum=5) at count.c:8
#3 <signal handler called>
#4 0xb77c5424 in __kernel_vsyscall()
#5 0xb76d0553 in __write_nocancel() at ../sysdeps/unix/syscall-template.S:81
#6 0x08048537 in main() at count.c:49
看來通過int 80
碰巧都很好的系統調用,但寫調用使用內核的VIDSO/vsyscall某種程度上打破(我不知道這個功能,更接近here)。它可能與使用sysenter
而不是int 80
有關,也可能在進入內核時陷阱標誌存活。我不太清楚遞歸調用__kernel_vsyscall
的情況。我也不明白爲什麼在__kernel_vsyscall
函數中有int 80
的調用。
有沒有人有建議發生了什麼,以及如何解決這個問題?也許可以禁用VDSO/vsysicall?或者是否可以使用int 80
而不是sysenter
替代__kernel_vsyscall
函數?