title: Debugging of a C program (Redis server)
body: <h2>Premise</h2>
  
  <p>Hi,</p>
  
  <p>I received multiple reports from a Redis user that experienced server crashes, using a Redis stable release (latest, 2.4.6). The bug is strange since the user is not doing esoteric things, just working a lot with the sorted set type, and only with the ZADD, ZREM, and ZREVRANK commands. However it is strange that a bug like that, causing crashes after a few billion operations executed, was only experienced by a single user. Fortunately the user in question is extremely helpful and collaborated a lot in the tracking of the issue, so I was able to obtain many times logs with the exact sequence of operations performed by Redis, that I re-played locally without result, I also tried to write scripts to closely mimic the kind of work load, to perform in-depth code reviews of the skip list implementation, and so forth.</p>
  
  <p>Even after all this efforts no way to reproduce the issue locally.
  It is also worth to mention that at some point the the user started sending the exact same traffic to another box running the same Redis version, but compiled with another gcc, and running in different hardware: so far no issues in this second instance. Still I want to understand what is happening.</p>
  
  <p>So finally I setup a different strategy with the user and asked him to run Redis using gdb, in order to obtain a core file. Finally Redis crashed again and I now have both the core file and the executable. Unfortunately I forgot to ask the user to compile Redis without optimizations.</p>
  
  <p>I need the stack overflow community help since with GDB I reach some conclusion but I've no really idea what could be happening here: at some point a function computes a pointer, and when it calls another function magically that pointer is different, pointing to a memory location that does not hold the right kind of data.</p>
  
  <h2>GDB session</h2>
  
  <p>The original executable was compiled with GCC 4.4.5-8, this is a GDB session that shows my investigation:</p>
  
  <pre><code>gdb ./redis-server core.16525
  GNU gdb (GDB) 7.1-ubuntu
  [snip]
  Program terminated with signal 11, Segmentation fault.
  #0  0x00007f3d9ecd216c in __pthread_rwlock_tryrdlock (rwlock=0x1)
      at pthread_rwlock_tryrdlock.c:46
  46      pthread_rwlock_tryrdlock.c: No such file or directory.
          in pthread_rwlock_tryrdlock.c
  </code></pre>
  
  <p>Actually the strack trace shown is about a secondary thread doing nothing (you can safely consider Redis a single-threaded app, the other threads are only used to perform things like fsync() against a file descriptor without blocking), let's select the right one.</p>
  
  <pre><code>(gdb) info threads
    3 Thread 16525  zslGetRank (zsl=0x7f3d8d71c360, score=19.498544884710096, 
      o=0x7f3d4cab5760) at t_zset.c:335
    2 Thread 16527  0x00007f3d9ecd216c in __pthread_rwlock_tryrdlock (
      rwlock=0x6b7f5) at pthread_rwlock_tryrdlock.c:46
  * 1 Thread 16526  0x00007f3d9ecd216c in __pthread_rwlock_tryrdlock (rwlock=0x1)
      at pthread_rwlock_tryrdlock.c:46
  (gdb) thread 3
  [Switching to thread 3 (Thread 16525)]#0  zslGetRank (zsl=0x7f3d8d71c360, 
      score=19.498544884710096, o=0x7f3d4cab5760) at t_zset.c:335
  335     t_zset.c: No such file or directory.
          in t_zset.c
  (gdb) bt
  #0  zslGetRank (zsl=0x7f3d8d71c360, score=19.498544884710096, o=0x7f3d4cab5760)
      at t_zset.c:335
  #1  0x000000000042818b in zrankGenericCommand (c=0x7f3d9dcdc000, reverse=1)
      at t_zset.c:2046
  #2  0x00000000004108d4 in call (c=0x7f3d9dcdc000) at redis.c:1024
  #3  0x0000000000410c1c in processCommand (c=0x7f3d9dcdc000) at redis.c:1130
  #4  0x0000000000419d3f in processInputBuffer (c=0x7f3d9dcdc000)
      at networking.c:865
  #5  0x0000000000419e1c in readQueryFromClient (el=&lt;value optimized out&gt;, 
      fd=&lt;value optimized out&gt;, privdata=0x7f3d9dcdc000, 
      mask=&lt;value optimized out&gt;) at networking.c:908
  #6  0x000000000040d4a3 in aeProcessEvents (eventLoop=0x7f3d9dc47000, 
      flags=&lt;value optimized out&gt;) at ae.c:342
  #7  0x000000000040d6ee in aeMain (eventLoop=0x7f3d9dc47000) at ae.c:387
  #8  0x0000000000412a4f in main (argc=2, argv=&lt;value optimized out&gt;)
      at redis.c:1719
  </code></pre>
  
  <p>We also generated a backtrace. As you can see call() is dispatching the ZREVRANK command, so the zrankGenericCommand() is called with the client structure and the reverse=1 (since it is REV rank) argument. We can easily investigate to check what are the arguments of the ZREVRANK command.</p>
  
  <pre><code>(gdb) up
  #1  0x000000000042818b in zrankGenericCommand (c=0x7f3d9dcdc000, reverse=1)
      at t_zset.c:2046
  2046    in t_zset.c
  (gdb) print c-&gt;argc
  $8 = 3
  (gdb) print (redisClient*)c-&gt;argc
  $9 = (redisClient *) 0x3
  (gdb) print (char*)(redisClient*)c-&gt;argv[0]-&gt;ptr
  $10 = 0x7f3d8267ce28 "zrevrank"
  (gdb) print (char*)(redisClient*)c-&gt;argv[1]-&gt;ptr
  $11 = 0x7f3d8267ce48 "pc_stat.hkperc"
  (gdb) print (long)(redisClient*)c-&gt;argv[2]-&gt;ptr
  $12 = 282472606
  </code></pre>
  
  <p>So the actual command generating the crash was: <strong>ZREVRANK pc_stat.hkperc 282472606</strong>
  This is consistent with the client logs obtained by the user. Note that I casted the pointer to a long integer for the latest argument, since Redis encodes integers this way to save memory when possible.</p>
  
  <p>Now that's fine, it is now time to investigate the zrankGenericCommand() that called zslGetRan() that caused the actual crash. This is the C source code of zrankGenericCommand around like 2046:</p>
  
  <pre><code>  2036      } else if (zobj-&gt;encoding == REDIS_ENCODING_SKIPLIST) {
    2037          zset *zs = zobj-&gt;ptr;
    2038          zskiplist *zsl = zs-&gt;zsl;
    2039          dictEntry *de;
    2040          double score;
    2041  
    2042          ele = c-&gt;argv[2] = tryObjectEncoding(c-&gt;argv[2]);
    2043          de = dictFind(zs-&gt;dict,ele);
    2044          if (de != NULL) {
    2045              score = *(double*)dictGetEntryVal(de);
    2046              rank = zslGetRank(zsl,score,ele);
    2047              redisAssert(rank); /* Existing elements always have a rank. */
    2048              if (reverse)
    2049                  addReplyLongLong(c,llen-rank);
    2050              else
    2051                  addReplyLongLong(c,rank-1);
    2052          } else {
    2053              addReply(c,shared.nullbulk);
    2054          }
    2055      }
  </code></pre>
  
  <p>Ok this is how it works:</p>
  
  <ul>
  <li>We lookup a Redis key, containing a sorted set data type (lookup not included in the code). The Redis Object associated with the key is stored in the <strong>zobj</strong> local variable.</li>
  <li>The <strong>zobj</strong> ptr field is a pointer to a structure of type <strong>zset</strong> representing the sorted set.</li>
  <li>In turn the <strong>zset</strong> structure has two pointers, one points to an hash table, and one to a skip list. This is needed since We both provide element-to-score lookups in O(1) for which we need an hash table, but also we take the elements ordered so we use a skip list. In line 2038 the pointer to the skip list (represented by a <strong>zskiplist</strong> structure) is assigned to the <strong>zsl</strong> variable.</li>
  <li>Later we encode the third argument (line 2042), this is why we casted the value to a long to print it from the client structure.</li>
  <li>In line 2043 we lookup the element from the dictionary, and the operation succeeds since we know that the function zslGetRank() side the <strong>if</strong> branch gets called.</li>
  <li>Finally in line 2046 we call zslGetRank() with three arguments: the pointer to the skip list, the score of the element, and the element itself.</li>
  </ul>
  
  <p>Fine... now what is the pointer that zslGetRank() should receive in theory? We can easily investigate this manually looking up the Redis hash table. I hashed manually the key and it maps to bucket 62 of the hash table, let's see if it is true:</p>
  
  <pre><code>(gdb) print (char*)c-&gt;db-&gt;dict-&gt;ht-&gt;table[62]-&gt;key
  $13 = 0x7f3d9dc0f6c8 "pc_stat.hkperc"
  </code></pre>
  
  <p>Exactly as expected. Let's check the object associated:</p>
  
  <pre><code>(gdb) print *(robj*)c-&gt;db-&gt;dict-&gt;ht-&gt;table[62]-&gt;val
  $16 = {type = 3, storage = 0, encoding = 7, lru = 557869, refcount = 1, 
    ptr = 0x7f3d9de574b0}
  </code></pre>
  
  <p>Type = 3, Encoding = 7, it means: it is a sorted set, encoded as a skip list. Fine again.
  The sorted set address (ptr field) is 0x7f3d9de574b0, so we can inspect this as well:</p>
  
  <pre><code>(gdb) print *(zset*)0x7f3d9de574b0
  $17 = {dict = 0x7f3d9dcf6c20, zsl = 0x7f3d9de591c0}
  </code></pre>
  
  <p>So we have:</p>
  
  <ul>
  <li>The object associated to the key that points to a sorted set that is stored at address 0x7f3d9de574b0</li>
  <li>In turn this sorted set is implemented with a skiplist, at address 0x7f3d9de591c0 (zsl field)</li>
  </ul>
  
  <p>Now let's check if our two variables are set to the right values:</p>
  
  <pre><code>2037            zset *zs = zobj-&gt;ptr;
  2038            zskiplist *zsl = zs-&gt;zsl;
  
  (gdb) info locals
  zs = 0x7f3d9de574b0
  zsl = 0x7f3d9de591c0
  de = &lt;value optimized out&gt;
  ele = &lt;value optimized out&gt;
  zobj = &lt;value optimized out&gt;
  llen = 165312
  rank = &lt;value optimized out&gt;
  </code></pre>
  
  <p>Everything is perfect so far: the variable <strong>zs</strong> is set to 0x7f3d9de574b0 as expected, and so is the variable <strong>zsl</strong> pointing to the skiplist, that is set to 0x7f3d9de591c0.</p>
  
  <p>Now this variables are no touched in the course of the code execution:</p>
  
  <p>This are the only lines of code between the assignment of the variables and the call to the  zslGetRank() function:</p>
  
  <pre><code>2042            ele = c-&gt;argv[2] = tryObjectEncoding(c-&gt;argv[2]);
  2043            de = dictFind(zs-&gt;dict,ele);
  2044            if (de != NULL) {
  2045                score = *(double*)dictGetEntryVal(de);
  2046                rank = zslGetRank(zsl,score,ele);
  </code></pre>
  
  <p>Nobody is touching <strong>zsl</strong>, however if we check the stack trace we see that the zslGetRank() function gets called not with the address 0x7f3d9de591c0 as first argument, but with a different one:</p>
  
  <pre><code>#0  zslGetRank (zsl=0x7f3d8d71c360, score=19.498544884710096, o=0x7f3d4cab5760)
      at t_zset.c:335
  </code></pre>
  
  <p>If you read all this you are an hero, and the reward is very small, consisting in this question: do you have an idea, even considering that hardware failure is an option, about how this argument gets modified? It seems very unlikely that the object encoding function or the hash table lookup can corrupt the stack of the caller (but apparently the argument is inside registers already at the time of the call). My assembler is not great, so if you have some clue... it is very welcomed. I'll left you with an info registers output and a disassemble:</p>
  
  <pre><code>(gdb) info registers
  rax            0x6      6
  rbx            0x7f3d9dcdc000   139902617239552
  rcx            0xf742d0b6       4148351158
  rdx            0x7f3d95efada0   139902485245344
  rsi            0x7f3d4cab5760   139901256030048
  rdi            0x7f3d8d71c360   139902342775648
  rbp            0x7f3d4cab5760   0x7f3d4cab5760
  rsp            0x7fffe61a8040   0x7fffe61a8040
  r8             0x7fffe61a7fd9   140737053884377
  r9             0x1      1
  r10            0x7f3d9dcd4ff0   139902617210864
  r11            0x6      6
  r12            0x1      1
  r13            0x7f3d9de574b0   139902618793136
  r14            0x7f3d9de591c0   139902618800576
  r15            0x7f3d8267c9e0   139902157572576
  rip            0x42818b 0x42818b &lt;zrankGenericCommand+251&gt;
  eflags         0x10206  [ PF IF RF ]
  cs             0x33     51
  ss             0x2b     43
  ds             0x0      0
  es             0x0      0
  fs             0x0      0
  gs             0x0      0
  (gdb) disassemble zrankGenericCommand
  Dump of assembler code for function zrankGenericCommand:
     0x0000000000428090 &lt;+0&gt;:     mov    %rbx,-0x30(%rsp)
     0x0000000000428095 &lt;+5&gt;:     mov    %r12,-0x20(%rsp)
     0x000000000042809a &lt;+10&gt;:    mov    %esi,%r12d
     0x000000000042809d &lt;+13&gt;:    mov    %r14,-0x10(%rsp)
     0x00000000004280a2 &lt;+18&gt;:    mov    %rbp,-0x28(%rsp)
     0x00000000004280a7 &lt;+23&gt;:    mov    %rdi,%rbx
     0x00000000004280aa &lt;+26&gt;:    mov    %r13,-0x18(%rsp)
     0x00000000004280af &lt;+31&gt;:    mov    %r15,-0x8(%rsp)
     0x00000000004280b4 &lt;+36&gt;:    sub    $0x58,%rsp
     0x00000000004280b8 &lt;+40&gt;:    mov    0x28(%rdi),%rax
     0x00000000004280bc &lt;+44&gt;:    mov    0x23138d(%rip),%rdx        # 0x659450 &lt;shared+80&gt;
     0x00000000004280c3 &lt;+51&gt;:    mov    0x8(%rax),%rsi
     0x00000000004280c7 &lt;+55&gt;:    mov    0x10(%rax),%rbp
     0x00000000004280cb &lt;+59&gt;:    callq  0x41d370 &lt;lookupKeyReadOrReply&gt;
     0x00000000004280d0 &lt;+64&gt;:    test   %rax,%rax
     0x00000000004280d3 &lt;+67&gt;:    mov    %rax,%r14
     0x00000000004280d6 &lt;+70&gt;:    je     0x4280ec &lt;zrankGenericCommand+92&gt;
     0x00000000004280d8 &lt;+72&gt;:    mov    $0x3,%edx
     0x00000000004280dd &lt;+77&gt;:    mov    %rax,%rsi
     0x00000000004280e0 &lt;+80&gt;:    mov    %rbx,%rdi
     0x00000000004280e3 &lt;+83&gt;:    callq  0x41b270 &lt;checkType&gt;
     0x00000000004280e8 &lt;+88&gt;:    test   %eax,%eax
     0x00000000004280ea &lt;+90&gt;:    je     0x428110 &lt;zrankGenericCommand+128&gt;
     0x00000000004280ec &lt;+92&gt;:    mov    0x28(%rsp),%rbx
     0x00000000004280f1 &lt;+97&gt;:    mov    0x30(%rsp),%rbp
     0x00000000004280f6 &lt;+102&gt;:   mov    0x38(%rsp),%r12
     0x00000000004280fb &lt;+107&gt;:   mov    0x40(%rsp),%r13
     0x0000000000428100 &lt;+112&gt;:   mov    0x48(%rsp),%r14
     0x0000000000428105 &lt;+117&gt;:   mov    0x50(%rsp),%r15
     0x000000000042810a &lt;+122&gt;:   add    $0x58,%rsp
     0x000000000042810e &lt;+126&gt;:   retq   
     0x000000000042810f &lt;+127&gt;:   nop
     0x0000000000428110 &lt;+128&gt;:   mov    %r14,%rdi
     0x0000000000428113 &lt;+131&gt;:   callq  0x426250 &lt;zsetLength&gt;
     0x0000000000428118 &lt;+136&gt;:   testw  $0x3c0,0x0(%rbp)
     0x000000000042811e &lt;+142&gt;:   jne    0x4282b7 &lt;zrankGenericCommand+551&gt;
     0x0000000000428124 &lt;+148&gt;:   mov    %eax,%eax
     0x0000000000428126 &lt;+150&gt;:   mov    %rax,0x8(%rsp)
     0x000000000042812b &lt;+155&gt;:   movzwl (%r14),%eax
     0x000000000042812f &lt;+159&gt;:   and    $0x3c0,%ax
     0x0000000000428133 &lt;+163&gt;:   cmp    $0x140,%ax
     0x0000000000428137 &lt;+167&gt;:   je     0x4281c8 &lt;zrankGenericCommand+312&gt;
     0x000000000042813d &lt;+173&gt;:   cmp    $0x1c0,%ax
     0x0000000000428141 &lt;+177&gt;:   jne    0x428299 &lt;zrankGenericCommand+521&gt;
     0x0000000000428147 &lt;+183&gt;:   mov    0x28(%rbx),%r15
     0x000000000042814b &lt;+187&gt;:   mov    0x8(%r14),%r13
     0x000000000042814f &lt;+191&gt;:   mov    0x10(%r15),%rdi
     0x0000000000428153 &lt;+195&gt;:   mov    0x8(%r13),%r14
     0x0000000000428157 &lt;+199&gt;:   callq  0x41bcc0 &lt;tryObjectEncoding&gt;
     0x000000000042815c &lt;+204&gt;:   mov    0x0(%r13),%rdi
     0x0000000000428160 &lt;+208&gt;:   mov    %rax,0x10(%r15)
     0x0000000000428164 &lt;+212&gt;:   mov    %rax,%rsi
     0x0000000000428167 &lt;+215&gt;:   mov    %rax,%rbp
     0x000000000042816a &lt;+218&gt;:   callq  0x40ede0 &lt;dictFind&gt;
     0x000000000042816f &lt;+223&gt;:   test   %rax,%rax
     0x0000000000428172 &lt;+226&gt;:   je     0x428270 &lt;zrankGenericCommand+480&gt;
     0x0000000000428178 &lt;+232&gt;:   mov    0x8(%rax),%rax
     0x000000000042817c &lt;+236&gt;:   mov    %rbp,%rsi
     0x000000000042817f &lt;+239&gt;:   mov    %r14,%rdi
     0x0000000000428182 &lt;+242&gt;:   movsd  (%rax),%xmm0
     0x0000000000428186 &lt;+246&gt;:   callq  0x427fd0 &lt;zslGetRank&gt;
  =&gt; 0x000000000042818b &lt;+251&gt;:   test   %rax,%rax
     0x000000000042818e &lt;+254&gt;:   je     0x4282d5 &lt;zrankGenericCommand+581&gt;
     0x0000000000428194 &lt;+260&gt;:   test   %r12d,%r12d
     0x0000000000428197 &lt;+263&gt;:   je     0x4281b0 &lt;zrankGenericCommand+288&gt;
     0x0000000000428199 &lt;+265&gt;:   mov    0x8(%rsp),%rsi
     0x000000000042819e &lt;+270&gt;:   mov    %rbx,%rdi
     0x00000000004281a1 &lt;+273&gt;:   sub    %rax,%rsi
     0x00000000004281a4 &lt;+276&gt;:   callq  0x41a430 &lt;addReplyLongLong&gt;
     0x00000000004281a9 &lt;+281&gt;:   jmpq   0x4280ec &lt;zrankGenericCommand+92&gt;
     0x00000000004281ae &lt;+286&gt;:   xchg   %ax,%ax
     0x00000000004281b0 &lt;+288&gt;:   lea    -0x1(%rax),%rsi
     0x00000000004281b4 &lt;+292&gt;:   mov    %rbx,%rdi
     0x00000000004281b7 &lt;+295&gt;:   callq  0x41a430 &lt;addReplyLongLong&gt;
     0x00000000004281bc &lt;+300&gt;:   nopl   0x0(%rax)
     0x00000000004281c0 &lt;+304&gt;:   jmpq   0x4280ec &lt;zrankGenericCommand+92&gt;
     0x00000000004281c5 &lt;+309&gt;:   nopl   (%rax)
     0x00000000004281c8 &lt;+312&gt;:   mov    0x8(%r14),%r14
     0x00000000004281cc &lt;+316&gt;:   xor    %esi,%esi
     0x00000000004281ce &lt;+318&gt;:   mov    %r14,%rdi
     0x00000000004281d1 &lt;+321&gt;:   callq  0x417600 &lt;ziplistIndex&gt;
     0x00000000004281d6 &lt;+326&gt;:   test   %rax,%rax
     0x00000000004281d9 &lt;+329&gt;:   mov    %rax,0x18(%rsp)
     0x00000000004281de &lt;+334&gt;:   je     0x428311 &lt;zrankGenericCommand+641&gt;
     0x00000000004281e4 &lt;+340&gt;:   mov    %rax,%rsi
     0x00000000004281e7 &lt;+343&gt;:   mov    %r14,%rdi
     0x00000000004281ea &lt;+346&gt;:   callq  0x4175c0 &lt;ziplistNext&gt;
     0x00000000004281ef &lt;+351&gt;:   test   %rax,%rax
     0x00000000004281f2 &lt;+354&gt;:   mov    %rax,0x10(%rsp)
     0x00000000004281f7 &lt;+359&gt;:   je     0x4282f3 &lt;zrankGenericCommand+611&gt;
     0x00000000004281fd &lt;+365&gt;:   mov    0x18(%rsp),%rdi
     0x0000000000428202 &lt;+370&gt;:   mov    $0x1,%r13d
     0x0000000000428208 &lt;+376&gt;:   lea    0x10(%rsp),%r15
     0x000000000042820d &lt;+381&gt;:   test   %rdi,%rdi
     0x0000000000428210 &lt;+384&gt;:   jne    0x428236 &lt;zrankGenericCommand+422&gt;
     0x0000000000428212 &lt;+386&gt;:   jmp    0x428270 &lt;zrankGenericCommand+480&gt;
     0x0000000000428214 &lt;+388&gt;:   nopl   0x0(%rax)
     0x0000000000428218 &lt;+392&gt;:   lea    0x18(%rsp),%rsi
     0x000000000042821d &lt;+397&gt;:   mov    %r14,%rdi
     0x0000000000428220 &lt;+400&gt;:   mov    %r15,%rdx
     0x0000000000428223 &lt;+403&gt;:   callq  0x425610 &lt;zzlNext&gt;
     0x0000000000428228 &lt;+408&gt;:   mov    0x18(%rsp),%rdi
     0x000000000042822d &lt;+413&gt;:   test   %rdi,%rdi
     0x0000000000428230 &lt;+416&gt;:   je     0x428270 &lt;zrankGenericCommand+480&gt;
     0x0000000000428232 &lt;+418&gt;:   add    $0x1,%r13
     0x0000000000428236 &lt;+422&gt;:   mov    0x8(%rbp),%rsi
     0x000000000042823a &lt;+426&gt;:   movslq -0x8(%rsi),%rdx
     0x000000000042823e &lt;+430&gt;:   callq  0x417a40 &lt;ziplistCompare&gt;
     0x0000000000428243 &lt;+435&gt;:   test   %eax,%eax
     0x0000000000428245 &lt;+437&gt;:   je     0x428218 &lt;zrankGenericCommand+392&gt;
     0x0000000000428247 &lt;+439&gt;:   cmpq   $0x0,0x18(%rsp)
     0x000000000042824d &lt;+445&gt;:   je     0x428270 &lt;zrankGenericCommand+480&gt;
     0x000000000042824f &lt;+447&gt;:   test   %r12d,%r12d
     0x0000000000428252 &lt;+450&gt;:   je     0x428288 &lt;zrankGenericCommand+504&gt;
     0x0000000000428254 &lt;+452&gt;:   mov    0x8(%rsp),%rsi
     0x0000000000428259 &lt;+457&gt;:   mov    %rbx,%rdi
     0x000000000042825c &lt;+460&gt;:   sub    %r13,%rsi
     0x000000000042825f &lt;+463&gt;:   callq  0x41a430 &lt;addReplyLongLong&gt;
     0x0000000000428264 &lt;+468&gt;:   jmpq   0x4280ec &lt;zrankGenericCommand+92&gt;
     0x0000000000428269 &lt;+473&gt;:   nopl   0x0(%rax)
     0x0000000000428270 &lt;+480&gt;:   mov    0x2311d9(%rip),%rsi        # 0x659450 &lt;shared+80&gt;
     0x0000000000428277 &lt;+487&gt;:   mov    %rbx,%rdi
     0x000000000042827a &lt;+490&gt;:   callq  0x419f60 &lt;addReply&gt;
     0x000000000042827f &lt;+495&gt;:   jmpq   0x4280ec &lt;zrankGenericCommand+92&gt;
     0x0000000000428284 &lt;+500&gt;:   nopl   0x0(%rax)
     0x0000000000428288 &lt;+504&gt;:   lea    -0x1(%r13),%rsi
     0x000000000042828c &lt;+508&gt;:   mov    %rbx,%rdi
     0x000000000042828f &lt;+511&gt;:   callq  0x41a430 &lt;addReplyLongLong&gt;
     0x0000000000428294 &lt;+516&gt;:   jmpq   0x4280ec &lt;zrankGenericCommand+92&gt;
     0x0000000000428299 &lt;+521&gt;:   mov    $0x44939f,%edi
     0x000000000042829e &lt;+526&gt;:   mov    $0x808,%edx
     0x00000000004282a3 &lt;+531&gt;:   mov    $0x44a674,%esi
     0x00000000004282a8 &lt;+536&gt;:   callq  0x432010 &lt;_redisPanic&gt;
     0x00000000004282ad &lt;+541&gt;:   mov    $0x1,%edi
     0x00000000004282b2 &lt;+546&gt;:   callq  0x40c3a0 &lt;_exit@plt&gt;
     0x00000000004282b7 &lt;+551&gt;:   mov    $0x44a7d0,%edi
     0x00000000004282bc &lt;+556&gt;:   mov    $0x7da,%edx
     0x00000000004282c1 &lt;+561&gt;:   mov    $0x44a674,%esi
     0x00000000004282c6 &lt;+566&gt;:   callq  0x432090 &lt;_redisAssert&gt;
     0x00000000004282cb &lt;+571&gt;:   mov    $0x1,%edi
     0x00000000004282d0 &lt;+576&gt;:   callq  0x40c3a0 &lt;_exit@plt&gt;
     0x00000000004282d5 &lt;+581&gt;:   mov    $0x448982,%edi
     0x00000000004282da &lt;+586&gt;:   mov    $0x7ff,%edx
     0x00000000004282df &lt;+591&gt;:   mov    $0x44a674,%esi
     0x00000000004282e4 &lt;+596&gt;:   callq  0x432090 &lt;_redisAssert&gt;
     0x00000000004282e9 &lt;+601&gt;:   mov    $0x1,%edi
     0x00000000004282ee &lt;+606&gt;:   callq  0x40c3a0 &lt;_exit@plt&gt;
     0x00000000004282f3 &lt;+611&gt;:   mov    $0x44a6e5,%edi
     0x00000000004282f8 &lt;+616&gt;:   mov    $0x7e2,%edx
     0x00000000004282fd &lt;+621&gt;:   mov    $0x44a674,%esi
     0x0000000000428302 &lt;+626&gt;:   callq  0x432090 &lt;_redisAssert&gt;
     0x0000000000428307 &lt;+631&gt;:   mov    $0x1,%edi
     0x000000000042830c &lt;+636&gt;:   callq  0x40c3a0 &lt;_exit@plt&gt;
     0x0000000000428311 &lt;+641&gt;:   mov    $0x44a6bd,%edi
     0x0000000000428316 &lt;+646&gt;:   mov    $0x7e0,%edx
     0x000000000042831b &lt;+651&gt;:   mov    $0x44a674,%esi
     0x0000000000428320 &lt;+656&gt;:   callq  0x432090 &lt;_redisAssert&gt;
     0x0000000000428325 &lt;+661&gt;:   mov    $0x1,%edi
     0x000000000042832a &lt;+666&gt;:   callq  0x40c3a0 &lt;_exit@plt&gt;
     End of assembler dump.
  </code></pre>
  
  <p>As requested, this is the tryObjectEncoding function:</p>
  
  <pre><code>/* Try to encode a string object in order to save space */
  robj *tryObjectEncoding(robj *o) {
      long value;
      sds s = o-&gt;ptr;
  
      if (o-&gt;encoding != REDIS_ENCODING_RAW)
          return o; /* Already encoded */
  
      /* It's not safe to encode shared objects: shared objects can be shared
       * everywhere in the "object space" of Redis. Encoded objects can only
       * appear as "values" (and not, for instance, as keys) */
       if (o-&gt;refcount &gt; 1) return o;
  
      /* Currently we try to encode only strings */
      redisAssert(o-&gt;type == REDIS_STRING);
  
      /* Check if we can represent this string as a long integer */
      if (!string2l(s,sdslen(s),&amp;value)) return o;
  
      /* Ok, this object can be encoded...
       *
       * Can I use a shared object? Only if the object is inside a given
       * range and if this is the main thread, since when VM is enabled we
       * have the constraint that I/O thread should only handle non-shared
       * objects, in order to avoid race conditions (we don't have per-object
       * locking).
       *
       * Note that we also avoid using shared integers when maxmemory is used
       * because very object needs to have a private LRU field for the LRU
       * algorithm to work well. */
      if (server.maxmemory == 0 &amp;&amp; value &gt;= 0 &amp;&amp; value &lt; REDIS_SHARED_INTEGERS &amp;&amp;
          pthread_equal(pthread_self(),server.mainthread)) {
          decrRefCount(o);
          incrRefCount(shared.integers[value]);
          return shared.integers[value];
      } else {
          o-&gt;encoding = REDIS_ENCODING_INT;
          sdsfree(o-&gt;ptr);
          o-&gt;ptr = (void*) value;
          return o;
      }
  }
  </code></pre>