2014-05-13 2 views
3

내 응용 프로그램의 구성을 저장하기 위해 uthash.h을 사용하고 있습니다. 는 config 실행시 읽기 파일에서 오면, 해시 내의 키와 값이 모두 동적으로 char * 년대를 할당 :해시 테이블의 읽기가 잘못되었습니다.

typedef struct config_entry { 
    char *name; 
    char *value; 
    UT_hash_handle hh; 
} CONFIG_ENTRY; 

으로는 사용 설명서에 설명, 나는 키를 추가하는 내 자신의 기능을 구현 고유성을 보장하는 config-hash에 추가합니다. 여기에 있습니다 :

void cfg_put(char *name, char *value, FREE_FLAGS flags) { 

    CONFIG_ENTRY *entry; 

    //first, check if the key is already in the hash 
    HASH_FIND_STR(config_, name, entry); 
    if(entry == NULL) { 
     //key doesn't exist yet => create new one 
     entry = (CONFIG_ENTRY *)malloc(sizeof(CONFIG_ENTRY)); 
     entry->name = name; 
     HASH_ADD_KEYPTR(hh, config_, entry->name, strlen(entry->name), entry); 
    } else { 
     //key exists => possibly free existing pointers before setting value 

     if((flags & FREE_NAME) == FREE_NAME) {  // 
      free(entry->name);      // these lines seem to be 
     }            // problematic. 
     entry->name = name;        // 

     if((flags & FREE_VALUE) == FREE_VALUE) { 
      free(entry->value); 
     } 
    } 

    //Finally, set the value 
    entry->value = value; 
} 

또한 구현을 점검하기위한 몇 가지 테스트 케이스를 작성했으며 제대로 실행되는 것 같습니다.

void test_config1(void) { 

    cfg_clear(FREE_ALL); 

    cfg_put(strdup("foo"), "bar", FREE_NONE); 
    CU_ASSERT_EQUAL(cfg_count(), 1); 
    CU_ASSERT_STRING_EQUAL(cfg_get("foo"), "bar"); 

    cfg_dump(); 

    cfg_put("foo", "baz", FREE_NAME); 
    CU_ASSERT_EQUAL(cfg_count(), 2); 
    CU_ASSERT_STRING_EQUAL(cfg_get("foo"), "baz"); 

    cfg_clear(FREE_NONE); 

    cfg_dump(); 
} 

cfg_get :

==2561== Invalid read of size 1 
==2561== at 0x4026097: bcmp (mc_replace_strmem.c:541) 
==2561== by 0x804ADF5: cfg_get (in /home/gj/..../test/config_test) 
==2561== by 0x804B2C7: test_config1 (in /home/..../test/config_test) 
==2561== by 0x402E446: run_single_test (in /usr/local/lib/libcunit.so.1.0.1) 
[...] 
==2561== Address 0x4194210 is 0 bytes inside a block of size 4 free'd 
==2561== at 0x4023B6A: free (vg_replace_malloc.c:366) 
==2561== by 0x804A872: cfg_put (in /home/..../test/config_test) 
==2561== by 0x804B27D: test_config1 (in /home/..../test/config_test) 
==2561== by 0x402E446: run_single_test (in /usr/local/lib/libcunit.so.1.0.1) 
[...] 

다음은 테스트 케이스 및 완전성에 대한 cfg_get의 구현이다 : 나는 memleaks를 확인하기 위해 Valgrind의를 사용하여 테스트를 실행하는 경우에는, 나는 항상 다음을 얻을

char *cfg_get(const char *name) { 

    CONFIG_ENTRY *entry = NULL; 
    HASH_FIND_STR(config_, name, entry); 

    if(entry) { 
     return entry->value; 
    } else { 
     return NULL; 
    } 
} 

어쨌든, 나는 name -pointer에서 cfg_get에 액세스하고있는 것 같습니다. 나는 그것을 cfg_put에 겹쳐 썼다. 이 문제는 name에만 발생하고 value에는 발생하지 않습니다. 나는 그것을 알아 내기에는 너무 어리 석다.

+0

프로그램이 멀티 스레드입니까? 여기서 문제가 될 수 있습니다. '휘발성'플래그가없는 변수는 레지스터로 이동할 수 있으며 원래 메모리 위치가 오래된 것일 수 있습니다. 또한'-O' 레벨이 버그에 영향을 미칩니 까? – Grapsus

+0

@Grapsus 아니요, 관련된 스레드가 없습니다. 나는 또한 아무것도 바꾸지 않은 -O0로 빌드를했다. – DeX3

답변

0

완전한 프로그램, 즉 valgrind 문제를 재현하는 완전한 최소한의 예제를 제공해야합니다. 질문에 게시 한 코드가 정상적으로 보이므로 버그가 다른 곳에 숨겨져 있어야합니다. 예 : 코드 또는 cfg_count().

는 (원래 나는 cfg_count()return HASH_COUNT(config_);해야한다는 생각 -하지만 구현하여 테스트 케이스를 통과하지 것이다, 그래서 당신은 괴상 일을해야 cfg_count 어쨌든 아마 그 함수에 대한 잘못된 이름입니다 의미합니다.).


문체, 당신은 당신이 전역 변수의 사용 (config_)을 피할 경우 디버깅 쉽게 코드를 찾을 수 있습니다, 당신은 직접 함께 비트를 "이 값을 확보 할 필요성"을 저장하면 확실히 당신은 쉽게 찾을 것 사용자가를 추적하도록 요구하는 대신 "value"비트, FREE_VALUE 등이 있습니다. 그것은 당신이 당신의 테스트 케이스가된다 시점에서 단지

typedef struct config_entry { 
    char *name; 
    char *value; 
    UT_hash_handle hh; 
    bool must_free_name; 
    bool must_free_value; 
} CONFIG_ENTRY; 

void cfg_put(char *name, char *value, FREE_FLAGS flags); 
void cfg_clear(void); 

을 제공해야한다 대신

typedef struct config_entry { 
    char *name; 
    char *value; 
    UT_hash_handle hh; 
} CONFIG_ENTRY; 

void cfg_put(char *name, char *value, FREE_FLAGS flags); 
void cfg_clear(FREE_FLAGS flags); 

의, 인 더 관리

당신의 config_put() 기능에 문제가 있습니다
void test_config1() 
{ 
    cfg_clear(); // use the stored bits to figure out what needs freeing 
    cfg_put(strdup("foo"), "bar", FREE_NAME); // name is alloc'ed, so name must be freed later 
    CU_ASSERT_EQUAL(cfg_count(), 1); 
    CU_ASSERT_STRING_EQUAL(cfg_get("foo"), "bar"); 

    cfg_put("foo", "baz", FREE_NONE); // neither name nor value is alloc'ed 
    CU_ASSERT_EQUAL(cfg_count(), 2); 
    CU_ASSERT_STRING_EQUAL(cfg_get("foo"), "baz"); 
} 
0

: 그것은 이미 해시에 삽입 된 항목의 키를 수정합니다. 너는 이것을하지 않아야한다. name 포인터를 동일한 문자열을 가리키는 포인터로 변경하는 것이 좋지만, 그렇지 않을 수도 있습니다. uthash.h의 구현은 약간 애매합니다.

나는 당신이 config_ 해시 모든 문자열을 소유시키는, API가 너무 config_put() 모든 문자열 관리를 수행 변경하고 더 이상 test_config1strdup() 전화를 제안합니다. 이것은 훨씬 간단하며 해시 구조 외부의 문자열 값의 수명주기를 잠재적으로 복잡하고 오류가 발생하기 쉬운 것으로 추적하지 않습니다.

void cfg_put(const char *name, const char *value) { 

    CONFIG_ENTRY *entry; 

    //first, check if the key is already in the hash 
    HASH_FIND_STR(config_, name, entry); 
    if (entry == NULL) { 
     //key doesn't exist yet => create new one 
     entry = malloc(sizeof(*entry)); 
     entry->name = strdup(name); 
     HASH_ADD_KEYPTR(hh, config_, entry->name, strlen(entry->name), entry); 
    } else { 
     //key exists => free existing value pointer if any 
     free(entry->value); 
    } 

    //Finally, set the value 
    entry->value = value ? strdup(value) : NULL; 
} 
관련 문제