2009-11-10 4 views
8

실패한 코드 경로를 테스트하는 가장 좋은 방법은 무엇입니까 malloc()? 당신이실패한 malloc()에 대한 단위 테스트

thingy *my_thingy = malloc(sizeof(thingy)); 
if (my_thingy == NULL) { 
    fprintf(stderr, "We're so screwed!\n"); 
    exit(EXIT_FAILURE); 
} 

같은 일을하고 있기 때문에 대부분의 경우, 아마 문제가되지 않지만 캐싱 또는 무엇이든을위한 몇 가지 여분의 물건을 할당했기 때문에 어떤 경우에 당신은 죽어가는 것보다 다른 선택을하고 그 기억을 되 찾을 수 있습니다.

그러나 오류가있는 malloc()에서 복구하기 위해 시도 할 수있는 경우에는 매우 드문 코드 경로에서 오류가 발생하여 테스트가 특히 중요합니다. 이 일을 실제로 어떻게 수행합니까?

+2

malloc()을 납치하여 때로는 0을 반환 할 수 있습니다. –

+2

'printf'와 같은 많은 라이브러리 함수는 프로세스의 메모리가 부족한 경우 실패 할 수 있습니다. – ephemient

+0

@ephemient'fprintf()'가 올바르게 처리하면 괜찮습니다. ;-) –

답변

15

S. Paavolainen이 나에게 제시 한이 문제에 대한 멋진 해결책을 발견했습니다. 아이디어는

  1. 스택이 데이터베이스에 존재하는 경우 malloc()
  2. 검사를 호출하는 스레드의 현재 실행 스택을 읽는 사용자 정의 할당에 의해, 당신은 단지 링커에서 할 수있는 표준 malloc()을 무시하는 것입니다 그는 하드 디스크
    1. 스택이 존재하지 않는 경우에 저장되어있는 데이터베이스에 스택을 추가하고 스택이 이미 존재 않은 경우 NULL
    2. 을 반환, 일반적으로 메모리를 할당하고
    3. 를 반환

그런 다음 당신은 당신의 단위 테스트를 여러 번 실행 :이 시스템은 자동으로 malloc() 실패로 다른 제어 경로를 통해 열거하고 훨씬 더 효율적이고 신뢰할 수있는, 예를 들어보다 무작위 테스트.

+0

_Nice_ 답변. 문제를 발견 할 기회에 의존하지 않으며 할당 실패의 결과를 체계적으로 테스트 할 수 있습니다. – quark

+1

+1 무작위 테스트를 완벽하게 지원합니다. SQLite는 http://www.sqlite.org/malloc.html#testing과 비슷한 기능을 수행합니다. malloc() 실패는 스택의 고유성을 검사하는 대신 카운터를 사용하여 트리거됩니다. –

+0

이것에 대한 이론은 견고하며이를 구현하고 싶습니다. 귀하의 대답은 실제로 구현 세부 사항이 없었으며 S. Paavolainen malloc에 ​​대한 검색에서도 아무 것도 제기하지 않았습니다. 몇 가지 구현 세부 사항을 제공 할 수 있습니까? –

1

FreeBSD에서 나는 C 라이브러리 malloc.o 모듈 (기호가 약함)에 과부하를 걸었고 malloc() 구현을 실패 확률을 제어 한 것으로 대체했습니다. 그래서 나는 정적으로 연결하고 테스트를 수행하기 시작했습니다. srandom()은 제어 된 의사 랜덤 시퀀스로 그림을 완성합니다.

또한 내 생각에 필요한 도구 집합에 대해서는 here으로 보입니다. 적어도 누수를 추적하기 위해 malloc()/free()를 오버로드하므로 원하는 모든 것을 추가 할 수있는 유용한 지점으로 보입니다.

1

이것은 좀 심한이지만, 당신이 정말로 단위 테스트를 원하는 경우, 당신은 위해서 #ifdefs와 함께 할 수 있습니다 :

thingy *my_thingy = malloc(sizeof(thingy)); 
#ifdef MALLOC_UNIT_TEST_1 
my_thingy = NULL; 
#endif 
if (my_thingy == NULL) { 
    fprintf(stderr, "We're so screwed!\n"); 
    exit(EXIT_FAILURE); 
} 

불행하게도,이 솔루션을 많이 다시 컴파일해야 할 것이다.

linux를 사용하는 경우 ulimit을 사용하여 메모리 부족으로 코드를 실행하는 것을 고려할 수 있지만주의해야합니다.

2

특별한 malloc 코드에 대한 특정 함수를 생성하여 실패 할 수 있으며 정상적으로 처리 할 수 ​​있다고 제안합니다. 예를 들면 다음과 같습니다.

void* special_malloc(size_t bytes) { 
    void* ptr = malloc(bytes); 
    if(ptr == NULL) { 
    /* Do something crafty */ 
    } else { 
    return ptr; 
    } 
} 

그런 다음 잘못된 값을 바이트로 전달하여 단위 테스트를 수행 할 수 있습니다. 이 라이브러리를 별도의 라이브러리에 넣고이 라이브러리를 호출하는 함수를 테스트 할 때 특별한 동작을하는 모의 라이브러리를 만들 수 있습니다.

+2

/* 교활한 작업 */ /* ???? */ /* PROFIT! */ – Derek

2

무작위 (중 staticly 연결 또는 명시 적으로 dlopened) 실패 또는 실제의 malloc을 호출하여 malloc을 구현 자신 만의 라이브러리를 쓰기

는이

+0

나는이 방법으로 끝났다. 내 테스트 프레임 워크와 함께 사용하려면 몇 가지 추가 컴파일 플래그가 필요하지만 매우 유연합니다. 또한, 내 공유 객체 라이브러리에서 malloc을 무작위로 실패하는 대신 malloc이 실패 할 때 전역 값을 선언했습니다. 이 변수는 테스트 코드에서 'extern'으로 선언되어야합니다. –

1

당신은 몇 가지 정의하고 글로벌 매개 변수를 사용하여 malloc을 납치 할 수 LD_PRELOAD 그것을 통제해라. .. 그것은 조금 hackish하지만 일하는 것처럼 보인다.

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

#define malloc(x) fake_malloc(x) 

struct { 
    size_t last_request; 
    int should_fail; 
    void *(*real_malloc)(size_t); 
} fake_malloc_params; 

void *fake_malloc(size_t size) { 
    fake_malloc_params.last_request = size; 
    if (fake_malloc_params.should_fail) { 
    return NULL; 
    } 
    return (fake_malloc_params.real_malloc)(size);; 
} 

int main(void) { 
    fake_malloc_params.real_malloc = malloc; 
    void *ptr = NULL; 
    ptr = malloc(1); 
    printf("last: %d\n", (int) fake_malloc_params.last_request); 
    printf("ptr: 0x%p\n", ptr); 
    return 0; 
}