2015-02-16 18 views
6

Tôi đang cố tạo móc trên chức năng hệ thống open(). Tôi đã thực hiện điều này dọc theo các dòng sau.Lỗi phân đoạn từ móc khi mở()

Tôi tạo ra một thư viện wrapper như sau:

extern int mocked_open(const char* fn, int flags, va_list args); 

int open(const char* fn, int flags, ...) 
{ 
    int r = -1; 
    va_list args; 

    va_start(args, flags); 
    r = mocked_open(fn, flags, args); 
    va_end(args); 

    return r; 
} 

tôi biên dịch này vào libwrapper.so, mà tôi tải sử dụng LD_PRELOAD.

Việc thực hiện mocked_open() là như sau (tôi sử dụng khuôn khổ CPPUtest):

int mocked_open(const char* fn, int flags, va_list args) 
{ 
    if (strncmp(fn, test_device_id, 11) == 0) 
    { 
     return mock().actualCall("open").returnValue().getIntValue(); 
    } 
    else 
    { 
     int r = -1; 
     int (*my_open)(const char*, int, ...); 
     void* fptr = dlsym(RTLD_NEXT, "open"); 
     memcpy(&my_open, &fptr, sizeof(my_open)); 

     if (flags & O_CREAT) 
     { 
      r = my_open(fn, flags, va_arg(args, mode_t)); 
     } 
     else 
     { 
      r = my_open(fn, flags); 
     } 

     return r; 
    } 
} 

Các test_device_id là một chuỗi đơn giản ("test_device"), mà tôi hy vọng không được sử dụng ở những nơi khác.

Trong khi chạy thử nghiệm, lỗi thực thi có lỗi phân đoạn. Tôi đã theo dõi điều này xuống chức năng lược tả GCC, mà muốn mở/tạo một bó các tệp .gcda và gọi open() cho việc này.

Sau khi một số gỡ rối với strace (mỗi gợi ý dưới đây), tôi thấy rằng dòng r = my_open(fn, flags, va_arg(args, mode_t)); thực sự là thủ phạm. Nó được gọi là đệ quy, hoặc có vẻ như: Tôi thấy rất nhiều cuộc gọi đến dòng này, mà không có hàm trả về. Sau đó, một segfault. Tệp đang được mở là tệp .gcda tương ứng (để lược tả). Trong thực tế, segfault chỉ xảy ra với hồ sơ được kích hoạt.

+0

Tôi nghĩ rằng sự cố của bạn là xử lý variadic http://stackoverflow.com/questions/150543/forward-an-invocation-of-a-variadic-function-in-c – user590028

+0

@ user590028: bạn có thể xây dựng không? Tôi đã tìm thấy có điều gì đó sai với xử lý đối số variadic (như tôi đã viết), nhưng cái gì ...? – Ludo

+1

Loại cuộc gọi nào để 'mở()' hiển thị 'strace' được sử dụng trong các trường hợp thử nghiệm của bạn? – alk

Trả lời

4

Khi bạn biên dịch với gcov profiling kích hoạt, trình biên dịch chèn thêm mã vào chức năng của mình, để theo dõi trong đó mã đã được thực thi. Trong giả thô, mà chèn mã sẽ làm (trong số những thứ khác):

if (!output_file_has_been_opened) { 
    fd = open(output_filename, ...); 
    check_ok(fd); 
    output_file_has_been_opened = TRUE; 
    track_coverage(); 
} 

... vì vậy nếu các tập tin đầu ra vẫn chưa được mở thành công (như lúc bắt đầu chương trình của bạn), nó sẽ cố gắng mở nó ra. Thật không may, trong trường hợp này sẽ gọi hàm open() được giả lập của bạn - có cùng mã được chèn vào; vì tệp vẫn chưa được mở thành công và vì mã gcov không nhận thức được rằng có điều gì đó bất thường xảy ra, nó sẽ cố gắng gọi lại số open() - và đây là nguyên nhân gây ra đệ quy (và sự kiện segfault cuối cùng) kiệt sức).

+0

Hmm, tôi nghĩ rằng điều này giải thích phải có phần không đầy đủ, bởi vì thường gcov sẽ chỉ cố gắng để mở các tập tin khi nó viết đầu ra của nó - ví dụ như khi một chương trình thoát, nhưng cũng tại một số lần khác. Một cái gì đó phải được kích hoạt nó để viết các tập tin .gcda (do đó tại sao bạn đang thấy rằng mở xảy ra ở tất cả), nhưng nó không rõ ràng với tôi ngay bây giờ những gì được ... – psmears

+0

Giải thích này âm thanh rất hợp lý với tôi và là phù hợp với những quan sát của tôi. Tôi thấy các cuộc gọi đến 'mở' để mở tệp gcda _after_ các thử nghiệm của tôi đã hoàn thành. Tôi không chắc bao nhiêu mã CPPUTest teardown có, nhưng theo như tôi có thể nói, các cuộc gọi là khá nhiều vào cuối thực hiện chương trình. Lời giải thích của bạn tuy nhiên cũng ngụ ý rằng tôi không thể đặt một móc trên 'mở()' với hồ sơ được kích hoạt ... – Ludo

+0

@ Ludo: Cảm ơn! Thành thật mà nói tôi vẫn cảm thấy có một mảnh ghép hình bị thiếu: nó không rõ ràng với tôi những gì gây ra các công cụ gcov để viết ra tập tin của nó. Nếu chúng ta có thể hình dung ra điều đó, có thể có một cách để giữ móc 'mở()' với lược tả được kích hoạt. Gdb có cung cấp dấu vết ngăn xếp có ý nghĩa cho lỗi phân đoạn không? Trong mọi trường hợp, một giải pháp có thể là (nếu hệ thống xây dựng của bạn cho phép) biên dịch thư viện LD_PRELOAD mà không cần lược tả, nhưng phần còn lại của mã - bạn sẽ không nhận được dữ liệu gcov cho thư viện thử nghiệm, nhưng có lẽ điều này ít quan trọng hơn so với mã được thử nghiệm? – psmears

5

Hãy thử điều này

typedef int (*OpenFunction)(const char* fn, int flags, ...); 

sau đó

OpenFunction function; 
void  **pointer; 

pointer = (void **)&function; 
*pointer = dlsym(RTLD_NEXT, "open"); 

này hoàn tất một ví dụ làm việc

#include <stdio.h> 
#include <string.h> 
#include <stdlib.h> 
#include <dlfcn.h> 
#include <unistd.h> 
#include <fcntl.h> 

#include <errno.h> 

typedef int (*OpenFunction)(const char* fn, int flags, ...); 

int main(int argc, char **argv) 
{ 
    OpenFunction function; 
    void  *dl; 
    int   fd; 
    void  **pointer; 

    if (argc < 2) 
     return -1; 
    pointer = (void **)&function; 
    *pointer = dlsym(RTLD_NEXT, "open"); 

    fd = function(argv[1], O_RDONLY); 
    if (fd != -1) 
    { 
     printf("file opened succesfully\n"); 
     close(fd); 
    } 
    else 
    { 
     printf("%s: cannot open the file\n", strerror(errno)); 
    } 
    return 0; 
} 
+0

Tôi đọc ở đây (http://stackoverflow.com/questions/10519312/how-does-this-make-sense-void-fptr-dlsymhandle-my-function) sử dụng loại dàn diễn viên này không được khuyến khích đặc biệt. – Ludo

+1

Đối với bản ghi: sử dụng cách tiếp cận của bạn để nhận con trỏ hàm không tạo sự khác biệt, tôi vẫn nhận được lỗi phân đoạn. – Ludo

+0

@Ludo Tôi đã cập nhật nó trong trường hợp bạn không thích dàn diễn viên, và vì bạn vẫn nhận được lỗi phân đoạn, vấn đề là một số vấn đề khác, bởi vì ví dụ hoạt động chính xác. –

Các vấn đề liên quan