2014-11-22 63 views
3

我想創建一個程序的執行的完整指令跟蹤,收集一些統計等。我第一次嘗試使用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函數?

回答

1

回答自己的問題。 我沒有弄清楚發生了什麼或詳細解釋,但我找到了一個解決方法:禁用VDSO。這可以通過

sudo sysctl vm.vdso_enabled=0 

有了這個做,通過程序這整個單步工作,包括跨系統調用步進。免責聲明:如果事情變壞,不要責怪我。

編輯:更新我的Linux(32位x86)之後,此錯誤不再發生。也許這是一個固定的錯誤。