【标题党】记一个关于Redis-4.0.1版本下zslGetElementByRank函数的诡异问题
2017-11-13 12:01
399 查看
引子
人在桌前坐,bug天上来。昨天早上到了小组,正准备总结一下爬山之旅,东哥就给我发了一个bug,让我也帮忙瞅瞅。。。bug描述
是一个使用Redis跳跃表的demo,可以参照东哥在RedisDB上的求助贴
东哥在StackOverFlow上的提问
这个关于Redis的demo如下
zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank) { zskiplistNode *x; unsigned long traversed = 0; int i; x = zsl->header; for (i = zsl->level-1; i >= 0; i--) { while (x->level[i].forward && (traversed + x->level[i].span) <= rank) { traversed += x->level[i].span; x = x->level[i].forward; } if (traversed == rank) { return x; } } return NULL; } int main(int argc, char **argv) { zskiplistNode *node; zskiplist *zsl = zslCreate(); //create a skiplist /*插入操作*/ zslInsert(zsl, 65.5, sdsnew("tom")); //insert some data zslInsert(zsl, 87.5, sdsnew("jack")); zslInsert(zsl, 70.0, sdsnew("alice")); zslInsert(zsl, 95.0, sdsnew("tony")); node = zslGetElementByRank(zsl, 4); //get element by rank printf("%s->%f\n", node->ele, node->score); //这里直接coredump了 return 0; }
抽象出的核心代码如下
//在File func.c char *func(char *array) { char *x; x = array //这里的意思 n < N 并没有任何数组越界等问题 return x; } // 在File main.c int main(void) { char *array = (char *)malloc(sizeof(char) * N); char *node = func(array); }
而现在的问题是x的值和node的值并不相同,并且经过我在Linux上的多次实验,发现x和node的低32位相同!
而东哥则表示,若不调用zslGetElementByRank函数,而是直接展开(就是相当于手动inline)则没有问题,同时对于跳跃表的其他函数使用都很正常。。。。
更加诡异的debug
本着debug全靠灵感的思路,我选择将Redis的内存分配器由默认的jemalloc换成了libc的ptmalloc,重新编译之。$ make MALLOC=libc
结果居然一下子就好了。。。
于是就开始纠结为什么会这样。。。
娄神的最终击杀
最后本着试试看的心态请教了娄神,在结队debug的过程中发现了问题,在make时出现了这样的警告额。。。。
所以。。。
最终的原因
由于main函数所在的server.c并没有zslGetElementByRank的函数声明,所以编译器认为其返回的是int类型,node和x只有低32位作为一个int类型是正确的。而用到的其他函数,像zslFirstInRange则已经被声明好,所以可以正确运行。
但是为什么我们将jemalloc换成libc的ptmalloc就好了呢?
经过早上的实验,发现在我的Linux上,对于小对象,ptmalloc返回的地址很低,比如像0x1306010这样的一个地址,而jemalloc返回的地址却很高,像0x7f3c6e215000这样的。而对于大对象,两种malloc都会返回一个大地址。
所以,由于我们的疏忽,导致只有低32位的地址正确,所以ptmalloc这样的低地址就可以跑出正确结果,而jemalloc的高地址就因为前面的高32位丢失而直接coredump了。
后记
这个bug的解决让我一下子就想起了《Effective C++》中的Item 53:不要忽视编译器的警告。 即使是很熟悉的代码,一但有warning,也要仔细看看。在学习的路上,始终都要像那个最开始抱着VC++6.0的我一样,不忽视每个warning。相关文章推荐
- Redis 一个很诡异的问题(部署)
- 关于索引的一个诡异问题
- 一个关于JavaScript的诡异问题
- 今天碰到的一个关于redis的问题
- 一个关于MFMessageComposeViewController的ios7中的诡异问题 收件人视图黑色 和解决方式
- 关于redis高版本造成的问题
- 通过V8源码看一个关于JS数组排序的诡异问题
- Linux中4.0.1版本的redis和java连接出现的redis被保护的问题以及解决办法
- 关于网页显示的一个诡异问题
- 关于Java数组越界的一个诡异问题【leetcode204】
- laravel 5.2.45版本中遇到的一个关于session的问题
- 关于不同版本软件复用同一个脚本的问题
- 关于Django版本不对遇到的一个问题
- 关于VSAN 6.x版本里一个针对SSD性能不足时的写入保护导致强行Unmount VSAN磁盘问题
- jbuilder2006中web程序关于jdk版本的一个问题
- 关于使用jwSMTP库发送邮件的一个诡异问题
- workcount中遇到的一个问题(关于版本)
- 关于jquery版本为1.6+使用checkbox的checked属性来判断一个checkbox是否被选中问题
- 一个关于Media Player 跨iPhone版本的问题
- 一个关于Media Player 跨iPhone版本的问题