Не удается найти ошибку valgrind в программе, которая правильно завершает работу и возвращает правильный вывод - PullRequest
0 голосов
/ 02 июля 2018

Несмотря на то, что программа корректно завершает работу и выдает правильный вывод, а утечек памяти нет, valgrind выдаёт мне несколько ошибок такого рода:

Invalid read of size 1
==910==    at 0x108DD4: fnv_hash_function (user.c:24)
==910==    by 0x108E17: hash (user.c:29)
==910==    by 0x109A50: icl_hash_find (icl_hash.c:114)
==910==    by 0x1094DB: db_request (user.c:197)
==910==    by 0x108D2E: main (tuser.c:65)
==910==  Address 0x5416f50 is 0 bytes inside a block of size 15 free'd
==910==    at 0x4C2E10B: free (vg_replace_malloc.c:530)
==910==    by 0x109152: freeKey (user.c:138)
==910==    by 0x109CF2: icl_hash_delete (icl_hash.c:192)
==910==    by 0x109796: db_request (user.c:222)
==910==    by 0x108CF8: main (tuser.c:59)
==910==  Block was alloc'd at
==910==    at 0x4C2CEDF: malloc (vg_replace_malloc.c:299)
==910==    by 0x108BDC: main (tuser.c:35)

hash и fnv_hash_function определены таким образом

static inline unsigned int fnv_hash_function( void *key, int len ) {
    unsigned char *p = (unsigned char*)key;
    unsigned int h = 2166136261u;
    int i;
    for ( i = 0; i < len; i++ )
        h = ( h * 16777619 ) ^ p[i]; //this is the line 24
    return h;
}


unsigned int hash(void *key){
    return fnv_hash_function(key, strlen(key));
}

Полагаю, проблема в операторе ^ , но я не могу понять, в чем проблема, поскольку программа завершается с правильным выводом и без ошибок сегментации.

icl_hash_find не является написанной мной функцией, но находится внутри библиотеки, которую я использую, и определяется таким образом

void *
icl_hash_find(icl_hash_t *ht, void* key)
{
    icl_entry_t* curr;
    unsigned int hash_val;

    if(!ht || !key) return NULL;

    hash_val = (* ht->hash_function)(key) % ht->nbuckets;

    for (curr=ht->buckets[hash_val]; curr != NULL; curr=curr->next)
        if ( ht->hash_key_compare(curr->key, key))
            return(curr->data);

    return NULL;
}

Я попробовал valgrind на тестовом наборе этой библиотеки, и ошибок не было найдено, поэтому я сомневаюсь, что проблема в этом.

EDIT: Ключ выделяется в этом цикле for:

char * s; //string used as key
for(int i = 0; i < N; i++){

    s = (char *)malloc(NAMELEN * sizeof(char));
    sprintf(s, "Utente %d", i);
    u = create_user( s , i);

    if(!db_request(db, s, u, PUT)){
        perror("problema PUT");
        exit(EXIT_FAILURE);
    }
    .
    .
    .

РЕДАКТИРОВАТЬ 2: Вот тело db_request:

bool db_request(userbase_t *db, char * key, user_t * u, dbop_t op ){

if(db==NULL || key == NULL ||(op!=DELETE && u==NULL)){
    errno = EINVAL;
    return false;
}
int lock_index; //indice del lock del bucket
switch(op){
    //implementazione PUT
    case PUT : 
        lock_index = db -> table -> hash_function(key) % db->nlocks;
        WLOCK(&db->locks[lock_index])
        errno = 0;
        if(icl_hash_insert(db->table, key, (void *) u)==NULL){
            RWUNLOCK(&db->locks[lock_index])
            //la chiave e' gia' associata ad un utente
            if(errno == EINVAL){
                perror("key gia' presente");
            }
            return false;
        }
        RWUNLOCK(&db->locks[lock_index])
        return true;
    //implementazione GET
    case GET :
        lock_index = db -> table -> hash_function(key) % db->nlocks;
        RLOCK(&db->locks[lock_index])

        u = icl_hash_find(db->table, (void *)key );

        RWUNLOCK(&db->locks[lock_index]);
        return true;
    //implementazione update
    case UPDATE :
        //elimina il vecchio e aggiunge il nuovo
        lock_index = db -> table -> hash_function(key) % db->nlocks;
        WLOCK(&db->locks[lock_index]);

        if(icl_hash_delete(db->table, key, freeKey, freeUser)){
            perror("problema UPDATE (icl_hash_delete) ");
            RWUNLOCK(&db->locks[lock_index]);
            return false;
        }

        if (icl_hash_insert(db->table, key, (void *) u)==NULL){
            perror("problema UPDATE (icl_hash_insert)");
            RWUNLOCK(&db->locks[lock_index]);
            return false;
        }
    case DELETE :
        lock_index = db -> table -> hash_function(key) % db->nlocks;
        WLOCK(&db->locks[lock_index]);          

        if(icl_hash_delete(db->table, key, freeKey, freeUser)){
            perror("problema DELETE");
            RWUNLOCK(&db->locks[lock_index]);
            return false;
        }

        RWUNLOCK(&db->locks[lock_index]);
        return true;
    //mai raggiunto
    default :
        errno = EINVAL;
        perror("problema switch op");
        return false;

}}

Но я не могу найти проблему, я начинаю думать, что проблема в libl icl_hash.

Проблема возникает, когда я вызываю GET для элемента, который только что УДАЛЕН в тестовой функции.

if(!db_request(db, s , u ,DELETE)){
            perror("problema DELETE");
            exit(EXIT_FAILURE);
        };

//provo a ottenerlo di nuovo
//The error happens here
if(!db_request(db, s , u ,GET)){
    perror("GET");
    exit(EXIT_FAILURE);
};

Единственное, что делает get, это вызывает эту функцию:

void *
icl_hash_find(icl_hash_t *ht, void* key)
{
icl_entry_t* curr;
unsigned int hash_val;

if(!ht || !key) return NULL;

hash_val = (* ht->hash_function)(key) % ht->nbuckets;

for (curr=ht->buckets[hash_val]; curr != NULL; curr=curr->next)
    if ( ht->hash_key_compare(curr->key, key))
        return(curr->data);

return NULL;
}

Ответы [ 2 ]

0 голосов
/ 02 июля 2018

Ваш недействительный доступ к памяти, которая уже освобождена:

Invalid read of size 1
==910==    at 0x108DD4: fnv_hash_function (user.c:24)
==910==    by 0x108E17: hash (user.c:29)
==910==    by 0x109A50: icl_hash_find (icl_hash.c:114)
==910==    by 0x1094DB: db_request (user.c:197)
==910==    by 0x108D2E: main (tuser.c:65)
==910==  Address 0x5416f50 is 0 bytes inside a block of size 15 free'd
==910==    at 0x4C2E10B: free (vg_replace_malloc.c:530)
==910==    by 0x109152: freeKey (user.c:138)
==910==    by 0x109CF2: icl_hash_delete (icl_hash.c:192)
==910==    by 0x109796: db_request (user.c:222)
==910==    by 0x108CF8: main (tuser.c:59)
==910==  Block was alloc'd at
==910==    at 0x4C2CEDF: malloc (vg_replace_malloc.c:299)
==910==    by 0x108BDC: main (tuser.c:35)

Блок был освобожден при вызове icl_hash_delete(), выполненном в строке 222 в функции db_request() в файле user.c. Недопустимый доступ был сделан при вызове icl_hash_find() в строке 197 в db_request() в user.c. Вы говорите, что вам предоставлен код icl_hash*.

Без тела функции db_request() трудно быть уверенным в том, что происходит, но есть как минимум пара возможностей.

  1. У вас есть свисающий указатель на запись, которая была удалена, и вы все равно не должны ее использовать.
  2. Код в функциях icl_hash* неправильно обрабатывает данные после удаления хеш-записи.

Если поставщик функции icl_hash.c достаточно надежен, разумно предположить, что проблема в вашем коде в db_request(). Посмотрите внимательно на строки 197 и 222, в частности, и переменные (указатели), переданные в функции icl_hash_find() и icl_hash_delete(). Посмотрите снова на страницу руководства для этих функций, чтобы увидеть, каковы правила.

Если вы не уверены в качестве кода в icl_hash.c, вы должны создать себе меньший MCVE, который создает хеш-таблицу, добавляет несколько строк, находит несколько строк, удаляет некоторые записи и делает еще некоторые выводы. , Это поможет вам определить, есть ли проблема в вашем коде или в коде icl_hash.c.

Поскольку память была выделена в main(), а не db_request(), вам, возможно, придется пересмотреть то, что вы делаете на этом уровне, но я предполагаю, что вы передали этот указатель на db_request() и передали право собственности на него в хеш-таблицу, когда вы добавили запись, а спецификация icl_hash_delete() гласит, что она освободит память, переданную вами в хеш-таблицу, и вы по-прежнему случайно удерживаете указатель на освободившуюся память (аргумент для * 1039) * функция). Вы должны быть очень осторожны, чтобы знать, кому принадлежит какая память - и когда эта память была освобождена.

0 голосов
/ 02 июля 2018

Valgrind сообщает, что у вас проблема с функцией fnv_hash_function. В этой функции вы получаете доступ к памяти после ее освобождения (освобождения) в freeKey. Он даже говорит вам, что вы обращаетесь к нему точно в начале этого блока памяти. Вы распределили эту память в main (tuser.c:35). Ваша программа работает случайно. Он будет продолжать работать до тех пор, пока рассматриваемый блок памяти не окажется в начале страницы памяти, и это будет последний блок, освобожденный на этой странице - тогда страница будет (вероятно - в зависимости от стратегии распределителя) ) отображается из вашего рабочего пространства. После отмены сопоставления при доступе к нему (как в fnv_hash_function) ваша программа будет аварийно завершать работу именно в тот момент, когда вы попытаетесь получить к ней доступ.

Когда этот блок может закончиться первым блоком на странице и стать последним, который будет освобожден? - трудно сказать, что это может произойти после любых изменений, которые вы вносите в свой исходный код или базовые библиотеки (включая системные библиотеки, которые вы не контролируете).

...