c - Why does a GDB watchpoint stop on an irrelevant line when swapping binary tree nodes? -


i trying swap 2 nodes a , b in binary tree places stored in memory change tree topology not changed. added special handling swapping node parent, still seems doesn't work. i'm using valgrind vgdb can catch memory errors , consistent addresses. if have tree like

78   \    40   /  \ 5c   c5 

and try swap a=40 , b=5c, links messed up. specifically, 40->right. setting watchpoint on (watch -l), found 40->right being set 5c->right (null) memcpy should be, being changed a later if(a_l.left == b){ impossible. i've had watchpoint report wrong line before when using movq instead of movb in assembly, i'm pretty sure have sizes right time because didn't @ first , didn't make through swaps fixed , makes through around dozen. sanity check tree after every operation error here. here simplest demonstration manage:

#include <stdlib.h> #include <string.h> #include <assert.h>  typedef struct avl_node avl_node; struct avl_node{     avl_node *left, *right, *parent;     signed char balance;     char data[]; };  avl_node *avl_root(avl_node *n){     while(n && n->parent){         n = n->parent;     }     return n; }  inline static int avl_check_links(avl_node *n){     if(!n)return 1;     if(n->left){         if(n->left->parent != n){             return 0;         }         if(!avl_check_links(n->left)){             return 0;         }     }     if(n->right){         if(n->right->parent != n){             return 0;         }         if(!avl_check_links(n->right)){             return 0;         }     }     return 1; }  void avl_swap_nodes(avl_node *a, avl_node *b, size_t size){     avl_node a_l = *a, b_l = *b;     char tmp[sizeof(avl_node) + size];     memcpy(tmp, a, sizeof(avl_node) + size);     memcpy(a, b, sizeof(avl_node) + size);     memcpy(b, tmp, sizeof(avl_node) + size);     if(a_l.left){         a_l.left->parent = b;     }     if(a_l.right){         a_l.right->parent = b;     }     if(b_l.left){         b_l.left->parent = a;     }     if(b_l.right){         b_l.right->parent = a;     }     if(a_l.parent){         if(a_l.parent->left == a){             a_l.parent->left = b;         }else{             a_l.parent->right = b;         }     }     if(b_l.parent){         if(b_l.parent->left == b){             b_l.parent->left = a;         }else{             b_l.parent->right = a;         }     }     if(a_l.parent == b){         if(b_l.left == a){             b->left = a_l.left;             a->left = b;         }else{             b->right = a_l.right;             a->right = b;         }         a->parent = b_l.parent;         b->parent = a;     }else if(b_l.parent == a){//gdb stops here on watch -l a->right         if(a_l.left == b){             a->left = b_l.left;             b->left = a;         }else{             a->right = b_l.right;             b->right = a;         }         b->parent = a_l.parent;         a->parent = b;     }     assert(avl_check_links(avl_root(a)));     assert(avl_check_links(avl_root(b))); }  int main(void){     avl_node a, b, c, d;     = (avl_node){.right=&b};     b = (avl_node){.left=&c, .right=&d, .parent=&a};     c = (avl_node){.parent=&b};     d = (avl_node){.parent=&b};     assert(avl_check_links(avl_root(&a)));     avl_swap_nodes(&b, &c, 0); } 

why gdb stop on wrong line? think may have fact using vgdb: skips lines when single step. why a->right changed second time @ all? thank you.

you can file run reasonably recent versions of gcc, gdb, , valgrind doing gcc -g -o main main.c, valgrind --vgdb=yes --vgdb-error=0 ./main&, gdb main, tar rem | vgdb, b avl_swap_nodes, c, watch -l a->right, , rid of vgdb process neatly doing c repeatedly , ctrl-d or kill , ctrl-d.

i figured out , isn't fun i'm going answer own question. node swapping code wrong. here version works

#include <stddef.h> void avl_swap_nodes(avl_node *a, avl_node *b, size_t size){     avl_node a_l = *a, b_l = *b;     char tmp[offsetof(avl_node, data) + size];     memcpy(tmp, a, offsetof(avl_node, data) + size);     memcpy(a, b, offsetof(avl_node, data) + size);     memcpy(b, tmp, offsetof(avl_node, data) + size);     if(a_l.parent == b){         if(b_l.left == a){             a->left = b;         }else{             a->right = b;         }         b->parent = a;         if(a->parent){             if(a->parent->left == b){                 a->parent->left = a;             }else{                 a->parent->right = a;             }         }     }else if(b_l.parent == a){         if(a_l.left == b){             b->left = a;         }else{             b->right = a;         }         a->parent = b;         if(b->parent){             if(b->parent->left == a){                 b->parent->left = b;             }else{                 b->parent->right = b;             }         }     }else{         if(a->parent){             if(b->parent == a->parent){                 if(a->parent->left == b){                     a->parent->left = a;                     b->parent->right = b;                 }else{                     a->parent->right = a;                     b->parent->left = b;                 }             }else{                 if(a->parent->left == b){                     a->parent->left = a;                 }else{                     a->parent->right = a;                 }             }         }         if(b->parent && b->parent != a->parent){             if(b->parent->left == a){                 b->parent->left = b;             }else{                 b->parent->right = b;             }         }     }     if(a->left){         a->left->parent = a;     }     if(a->right){         a->right->parent = a;     }     if(b->left){         b->left->parent = b;     }     if(b->right){         b->right->parent = b;     }     assert_all(avl_root(a));     assert_all(avl_root(b)); } 

the reason why gdb reporting watchpoint on wrong line because previous memory write overflows. can happen example when use movq instead of movb in assembly, or when char a; ((int*)&a) = (int)0; in c, or when memcpy more meant to. last 1 causing problems in code. consider struct struct a{int a; char b[];);. sizeof(struct a) 8 because of structure padding, offsetof(struct a, b) 4. therefore if calculate size of struct a data in flexible array @ end adding data size sizeof(struct a), calculate value 4 bytes greater should be. solution use offsetof(struct a, b); instead.

the reason why gdb skipping lines because using valgrind --vgdb=yes instead of valgrind --vgdb=full.


Comments

Popular posts from this blog

java - nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet Hibernate+SpringMVC -

sql - Postgresql tables exists, but getting "relation does not exist" when querying -

asp.net mvc - breakpoint on javascript in CSHTML? -