2014-11-22 51 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)之后,此错误不再发生。也许这是一个固定的错误。