2013-04-15 54 views
10

Giả sử tôi có mã gây ra lỗi phân đoạn.in lý do lỗi phân đoạn

char * ptr = NULL; 
*ptr = "hello"; /* this will cause a segmentation fault */ 

Làm thế nào để in trên runtime, địa chỉ trong bộ nhớ mà các lỗi phân khúc xảy ra tại, và lý do cho lỗi segmentation (truy cập vào khu vực bộ nhớ bị cấm, hay cái gì khác).

Tôi đọc về tệp kết xuất lõi, nhưng tôi không chắc đó có phải là giải pháp đúng hay không.

Tôi làm cách nào để thực hiện việc này?

P.S, Tôi biết thực tế là tôi có thể đạt được điều đó bằng cách sử dụng gdb hoặc trình gỡ lỗi khác, nhưng mục đích là làm điều này bằng cách sử dụng mã và chỉ mã.

+3

Bạn có thể sử dụng chức năng ['backtrace'] (http://linux.die.net/man/3/backtrace). Nhưng tôi thực sự khuyên bạn nên chạy chương trình của bạn trong một trình gỡ lỗi thay thế, nó sẽ cho phép bạn không chỉ nhìn thấy các backtrace, nhưng đi bộ lên ngăn xếp cuộc gọi và kiểm tra các biến. –

+2

"đọc về tệp kết xuất lõi" - Tôi thực sự khuyên bạn nên sử dụng chúng. Họ đổ tất cả mọi thứ trong bộ nhớ và sau đó bạn có thể mở chúng với 'gdb' và thực thi đúng. Điều này sẽ cung cấp cho bạn cơ hội để xem chính xác những gì đã xảy ra (trừ khi bộ nhớ không bị rối loạn, nhưng đó là trường hợp khá hiếm) - xem bất kỳ biến 'giá trị, backtrace, chủ đề, vv (tất nhiên, nó sẽ được tốt đẹp để có gỡ lỗi tối đa mức độ và không tối ưu hóa cho loại hình điều tra này) –

+0

hmm .. loại '* ptr' là' char', nhưng '" hello "' là kiểu 'char *'. bạn có lẽ nên gán một ký tự ('* ptr = 'h';') hoặc sử dụng một 'memmove()' hoặc tương tự cho ví dụ là đúng.vì nó là, nó lấy địa chỉ của hằng số chuỗi, chuyển nó thành số nguyên, chuyển nó xuống 1 byte, và sau đó segfaults gán nó cho '* ptr' – SingleNegationElimination

Trả lời

4

Nếu bạn muốn biết nguyên nhân bạn có thể đăng ký sig handler NAL, một cái gì đó như:

void handler(int signum, siginfo_t *info, void *context) 
{ 
    struct sigaction action = { 
    .sa_handler = SIG_DFL, 
    .sa_sigaction = NULL, 
    .sa_mask = 0, 
    .sa_flags = 0, 
    .sa_restorer = NULL 
    }; 

    fprintf(stderr, "Fault address: %p\n", info->si_addr); 
    switch (info->si_code) { 
    case SEGV_MAPERR: 
    fprintf(stderr, "Address not mapped.\n"); 
    break; 

    case SEGV_ACCERR: 
    fprintf(stderr, "Access to this address is not allowed.\n"); 
    break; 

    default: 
    fprintf(stderr, "Unknown reason.\n"); 
    break; 
    } 

    /* unregister and let the default action occur */ 
    sigaction(SIGSEGV, &action, NULL); 
} 

Và rồi đâu đó bạn cần phải đăng ký nó:

struct sigaction action = { 
    .sa_handler = NULL, 
    .sa_sigaction = handler, 
    .sa_mask = 0, 
    .sa_flags = SA_SIGINFO, 
    .sa_restorer = NULL 
    }; 


    if (sigaction(SIGSEGV, &action, NULL) < 0) { 
    perror("sigaction"); 
    } 

Về cơ bản bạn đăng ký một dấu hiệu cho thấy vụ cháy khi SIGSEGV được phân phối, và bạn nhận được một số thông tin bổ sung, để báo giá xem man page:

The following values can be placed in si_code for a SIGSEGV signal: 

     SEGV_MAPERR address not mapped to object 

     SEGV_ACCERR invalid permissions for mapped object 

những bản đồ với hai lý do cơ bản f hoặc bị lỗi seg - trang bạn truy cập không được ánh xạ hoặc bạn không được phép thực hiện bất kỳ thao tác nào bạn đã cố gắng truy cập trang đó.

Ở đây sau khi trình xử lý tín hiệu kích hoạt, nó tự hủy đăng ký và thay thế hành động mặc định. Điều này khiến cho thao tác không thực hiện lại được vì vậy nó có thể bị bắt bởi tuyến đường bình thường. Đây là hành vi bình thường của lỗi trang (tiền thân để nhận lỗi seg) để những thứ như yêu cầu phân trang hoạt động.

2

Như đã trả lời ở đây: How to generate a stacktrace when my gcc C++ app crashes

Bạn có thể (trong trường hợp của GCC với Linux/BSD ít nhất) làm được điều này khá dễ dàng:

Ví dụ mã:

#include <stdio.h> 
#include <execinfo.h> 
#include <signal.h> 
#include <stdlib.h> 


void handler(int sig) { 
    void *array[10]; 
    size_t size; 

    // get void*'s for all entries on the stack 
    size = backtrace(array, 10); 

    // print out all the frames to stderr 
    fprintf(stderr, "Error: signal %d:\n", sig); 
    backtrace_symbols_fd(array, size, 2); 
    exit(1); 
} 

int main(int argc, char **argv) { 
    signal(SIGSEGV, handler); // install our handler 

    char * ptr = NULL; 
    *ptr = "hello"; /* this will cause a segmentation fault */ 
} 

Ví dụ đầu ra :

# gcc -g -rdynamic -o test test.c 
# ./test 
Error: signal 11: 
0 test        0x000000010e99dcfa handler + 42 
1 libsystem_c.dylib     0x00007fff95c1194a _sigtramp + 26 
2 ???         0x0000000000000000 0x0 + 0 
3 libdyld.dylib      0x00007fff8fa177e1 start + 0 
4 ???         0x0000000000000001 0x0 + 1 
+0

Điều này sẽ không tạo ra một bãi chứa lõi nữa, ít nhất không phải là một nguyên nhân ban đầu của lỗi. Tốt, bạn có một số loại stacktrace, nhưng với giá trị ít hơn nhiều. Không có hành vi phạm tội, bạn chúng tôi chỉ trả lời câu hỏi OP, nhưng cảnh báo vẫn áp dụng ;-). Ngoài ra, hãy xem xét sử dụng một sự kết hợp của 'sprintf' và' write (2, buf, ...) 'để tránh' fprintf' có thể phức tạp trong một trình xử lý tín hiệu (ít nhất là cho các tín hiệu async). Ngoài ra, tôi sẽ không gọi 'exit()' nhưng chỉ '_exit()' hoặc 'abort()'. Nhưng YMMV. –

+0

Tôi đồng ý với bạn Kitô giáo, cho là bạn có thể kích hoạt tính năng này cho một số bản dựng nếu bạn _really_ không muốn gửi một bãi chứa lõi. Nhưng cá nhân tôi sẽ chỉ quấn thực thi trong một kịch bản cho phép gdb (nếu có) hiển thị stacktrace từ dump cốt lõi được tạo ra ... Có vẻ là một giải pháp hợp lý hơn nhiều. – Wolph