在终止的程序上找不到 valgrind 错误并且 returns 正确输出正确
Can't find valgrind error on program which terminates and returns the right output correctly
尽管程序正确终止并给我正确的输出,并且没有内存泄漏,但 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,没有发现任何错误,所以我怀疑问题出在那里。
编辑:
密钥在此 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;
}}
但我找不到问题,我开始认为问题出在 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;
}
Valgrind 告诉您 fnv_hash_function
函数有问题。在此函数中,您可以在 freeKey
中释放(释放)内存后访问内存。它甚至会告诉您恰好在该内存块的开头访问它。您在 main (tuser.c:35)
中分配了该内存。你的程序是偶然运行的。只要有问题的内存块不会在内存页面的开头结束,它将继续工作,它将是该页面上释放的最后一个块 - 然后页面将(可能 - 取决于分配器策略) 从您的进程中取消映射 space。取消映射后,如果您访问它(如在 fnv_hash_function
中),您的程序将在您尝试访问它的那一刻恰好崩溃。
该块何时会成为页面上的第一个块并成为最后一个被释放的块? - 很难说它可能会在您对源代码或底层库(包括您无法控制的系统库)进行任何更改后发生。
您对已释放内存的无效访问:
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)
在文件 user.c
的函数 db_request()
的第 222 行对 icl_hash_delete()
的调用中释放了该块。在 user.c
的 db_request()
的第 197 行对 icl_hash_find()
的调用中进行了无效访问。你说icl_hash*
代码提供给你
没有函数体 db_request()
很难确定发生了什么,但至少有两种可能性。
- 您有一个指向已删除条目的悬空指针,您不应该继续使用它。
icl_hash*
函数中的代码在删除散列记录后错误处理数据。
如果 icl_hash.c
函数的提供者相当可靠,则可以明智地假设问题出在 db_request()
中的代码中。仔细查看第 197 行和第 222 行,以及传递给 icl_hash_find()
和 icl_hash_delete()
函数的变量(指针)。再次查看这些函数的手册页以了解规则是什么。
如果您不确定 icl_hash.c
中代码的质量,您应该自己创建一个较小的 MCVE,它创建一个散列 table,添加一些行,查找一些行,删除一些条目,并做一些更多的发现。这将帮助您确定您的代码或 icl_hash.c
代码是否存在问题。
由于内存是在 main()
而不是 db_request()
中分配的,您可能需要检查您在该级别所做的事情,但我的猜测是您将该指针传递给了 db_request()
并将它的所有权交给散列 table 当你添加一个条目时, icl_hash_delete()
的规范说它将释放你交给散列 table 的内存,你不小心仍然持有指向现在释放的内存的指针(db_request()
函数的参数)。您必须非常小心,以确保您知道谁拥有什么内存——以及该内存何时被释放。
尽管程序正确终止并给我正确的输出,并且没有内存泄漏,但 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,没有发现任何错误,所以我怀疑问题出在那里。
编辑: 密钥在此 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;
}}
但我找不到问题,我开始认为问题出在 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;
}
Valgrind 告诉您 fnv_hash_function
函数有问题。在此函数中,您可以在 freeKey
中释放(释放)内存后访问内存。它甚至会告诉您恰好在该内存块的开头访问它。您在 main (tuser.c:35)
中分配了该内存。你的程序是偶然运行的。只要有问题的内存块不会在内存页面的开头结束,它将继续工作,它将是该页面上释放的最后一个块 - 然后页面将(可能 - 取决于分配器策略) 从您的进程中取消映射 space。取消映射后,如果您访问它(如在 fnv_hash_function
中),您的程序将在您尝试访问它的那一刻恰好崩溃。
该块何时会成为页面上的第一个块并成为最后一个被释放的块? - 很难说它可能会在您对源代码或底层库(包括您无法控制的系统库)进行任何更改后发生。
您对已释放内存的无效访问:
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)
在文件 user.c
的函数 db_request()
的第 222 行对 icl_hash_delete()
的调用中释放了该块。在 user.c
的 db_request()
的第 197 行对 icl_hash_find()
的调用中进行了无效访问。你说icl_hash*
代码提供给你
没有函数体 db_request()
很难确定发生了什么,但至少有两种可能性。
- 您有一个指向已删除条目的悬空指针,您不应该继续使用它。
icl_hash*
函数中的代码在删除散列记录后错误处理数据。
如果 icl_hash.c
函数的提供者相当可靠,则可以明智地假设问题出在 db_request()
中的代码中。仔细查看第 197 行和第 222 行,以及传递给 icl_hash_find()
和 icl_hash_delete()
函数的变量(指针)。再次查看这些函数的手册页以了解规则是什么。
如果您不确定 icl_hash.c
中代码的质量,您应该自己创建一个较小的 MCVE,它创建一个散列 table,添加一些行,查找一些行,删除一些条目,并做一些更多的发现。这将帮助您确定您的代码或 icl_hash.c
代码是否存在问题。
由于内存是在 main()
而不是 db_request()
中分配的,您可能需要检查您在该级别所做的事情,但我的猜测是您将该指针传递给了 db_request()
并将它的所有权交给散列 table 当你添加一个条目时, icl_hash_delete()
的规范说它将释放你交给散列 table 的内存,你不小心仍然持有指向现在释放的内存的指针(db_request()
函数的参数)。您必须非常小心,以确保您知道谁拥有什么内存——以及该内存何时被释放。